summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Leonard <d@cvs.openbsd.org>1999-01-17 23:40:03 +0000
committerDavid Leonard <d@cvs.openbsd.org>1999-01-17 23:40:03 +0000
commit88445003a8b78125cabf9aafcd7b49f1af9a9f87 (patch)
treebcf1bc7e71f04e89a9036d7795a2a0c3422ae7f8
parent07aad870427e09b1a5320970648cd235915fa9b2 (diff)
some performance metrics
-rw-r--r--lib/libc_r/BENCH/Makefile16
-rw-r--r--lib/libc_r/BENCH/bench.h65
-rw-r--r--lib/libc_r/BENCH/cond_nowait.c35
-rw-r--r--lib/libc_r/BENCH/cond_timed.c78
-rw-r--r--lib/libc_r/BENCH/cond_wake.c58
-rw-r--r--lib/libc_r/BENCH/mutex_cont.c128
-rw-r--r--lib/libc_r/BENCH/mutex_nocont.c29
-rw-r--r--lib/libc_r/BENCH/null.c26
-rw-r--r--lib/libc_r/BENCH/once_overhead.c30
-rw-r--r--lib/libc_r/BENCH/self_overhead.c79
-rw-r--r--lib/libc_r/BENCH/yield.c55
11 files changed, 599 insertions, 0 deletions
diff --git a/lib/libc_r/BENCH/Makefile b/lib/libc_r/BENCH/Makefile
new file mode 100644
index 00000000000..22a9618d291
--- /dev/null
+++ b/lib/libc_r/BENCH/Makefile
@@ -0,0 +1,16 @@
+
+CPPFLAGS += -pthread
+LDFLAGS += -pthread
+CFLAGS += -O -ggdb
+METRICS = null once_overhead self_overhead mutex_nocont mutex_cont\
+ cond_nowait cond_wake cond_timed
+MKDEP = -p
+SRCS = ${METRICS:=.c}
+CLEANFILES += ${METRICS}
+
+all: ${METRICS}
+ @${SUDO} nice -n -19 ${SHELL} -c \
+ 'ulimit -d 65536; for m in ${METRICS}; do $$m || exit; done'
+
+
+.include <bsd.prog.mk>
diff --git a/lib/libc_r/BENCH/bench.h b/lib/libc_r/BENCH/bench.h
new file mode 100644
index 00000000000..1d68915a75e
--- /dev/null
+++ b/lib/libc_r/BENCH/bench.h
@@ -0,0 +1,65 @@
+
+#define BENCH_LOOPS (16384)
+#include <sys/time.h>
+
+typedef struct {
+ int i;
+ int n;
+ int divisor;
+ struct timespec start;
+ struct timespec end;
+ struct timespec elapsed;
+ double average;
+ char *name;
+ char *doc;
+ char *units;
+} bench_t;
+
+#define bench_now(tsp) \
+ clock_gettime(CLOCK_REALTIME, (tsp))
+
+/*
+ * Repeat the body of the loop 'max' times, with a few extra 'warm up'
+ * cycles to negate cache effects.
+ */
+#define bench_amortize(b, max) \
+ for ((b)->i = -64, \
+ (b)->n = (max); \
+ (b)->i < (b)->n; \
+ (b)->i ++, \
+ ((b)->i == 0 ? bench_now(&(b)->start) : \
+ ((b)->i == (b)->n ? bench_now(&(b)->end) \
+ :0))\
+ )
+
+#define bench_init(b, nm, dc, un) do { \
+ (b)->name = (nm); \
+ (b)->doc = (dc); \
+ (b)->units = (un); \
+ timespecclear(&(b)->start); \
+ timespecclear(&(b)->end); \
+ timespecclear(&(b)->elapsed); \
+ (b)->n = (b)->i = 0; \
+ (b)->divisor = 1; \
+} while (0)
+
+#define bench_header(b) \
+ printf("----------------------------------------------------\n" \
+ "Name:\t%s\nDesc:%s\n", (b)->name, (b)->doc)
+
+#define bench_report(b) do { \
+ struct timespec elapsed; \
+ bench_t overhead; \
+ \
+ /* compute the loop overhead */ \
+ bench_amortize(&overhead, BENCH_LOOPS) { /* nothing */ } \
+ \
+ timespecsub(&(b)->end, &(b)->start, &(b)->elapsed); \
+ (b)->average = ((double)(b)->elapsed.tv_sec * 1000000000.0 + \
+ (b)->elapsed.tv_nsec) / (double)((b)->divisor) / \
+ (double)((b)->n); \
+ \
+ printf("Time: %f usec %s\n", (b)->average, (b)->units); \
+ if ((b)->divisor != 1) \
+ printf("\t(%d operations per cycle)\n", (b)->divisor); \
+} while (0)
diff --git a/lib/libc_r/BENCH/cond_nowait.c b/lib/libc_r/BENCH/cond_nowait.c
new file mode 100644
index 00000000000..88c1769c502
--- /dev/null
+++ b/lib/libc_r/BENCH/cond_nowait.c
@@ -0,0 +1,35 @@
+#include <pthread.h>
+#include <string.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Condition Variable Signal/Broadcast, No Waiters";
+static char doc[] =
+"\tThis is the amount of time needed to execute pthread_cond_signal()\n"
+"\tor pthread_cond_broadcast() if there are no threads blocked on\n"
+"\tthe condition.";
+
+int
+main() {
+ pthread_cond_t c;
+ bench_t b;
+
+ bench_init(&b, name, doc, "per call of pthread_cond_signal()");
+ bench_header(&b);
+ pthread_cond_init(&c, NULL);
+ bench_amortize(&b, BENCH_LOOPS) {
+ pthread_cond_signal(&c);
+ }
+ bench_report(&b);
+
+ bench_init(&b, NULL, NULL, "per call of pthread_cond_broadcast()");
+ pthread_cond_init(&c, NULL);
+ bench_amortize(&b, BENCH_LOOPS) {
+ pthread_cond_broadcast(&c);
+ }
+ bench_report(&b);
+
+ exit(0);
+}
+
+
diff --git a/lib/libc_r/BENCH/cond_timed.c b/lib/libc_r/BENCH/cond_timed.c
new file mode 100644
index 00000000000..dc48cd7e340
--- /dev/null
+++ b/lib/libc_r/BENCH/cond_timed.c
@@ -0,0 +1,78 @@
+#include <pthread.h>
+#include <string.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Time of Wakeup After Timed Wait";
+static char doc[] =
+"\tThe tiem required for the highest-priority thread to rresume\n"
+"\texecution after a call to pthread_cond_timedwait(). Metrics\n"
+"\tare provided for both the cases when the pthread_cond_timedwait()\n"
+"\tcall is awakened by a call to pthread_cond_signal() and when\n"
+"\tthe absolute time to be awaited has already passed at the time\n"
+"\tof the call.";
+
+pthread_mutex_t m1, m2;
+pthread_cond_t c;
+bench_t b;
+struct timespec waketime;
+
+void *
+other_thread(arg)
+ void *arg;
+{
+
+ pthread_set_name_np(pthread_self(), "oth");
+ pthread_mutex_lock(&m2);
+
+ bench_amortize(&b, BENCH_LOOPS) {
+ pthread_cond_timedwait(&c, &m2, &waketime);
+ pthread_cond_signal(&c);
+ }
+ pthread_mutex_unlock(&m2);
+}
+
+int
+main() {
+ pthread_t other;
+ pthread_mutex_t m;
+ struct timespec ts;
+
+ bench_init(&b, name, doc, "from signal to wait inclusive");
+ b.n = BENCH_LOOPS;
+ bench_header(&b);
+ pthread_cond_init(&c, NULL);
+ pthread_mutex_init(&m1, NULL);
+ pthread_mutex_init(&m2, NULL);
+
+ clock_gettime(CLOCK_REALTIME, &waketime);
+ waketime.tv_sec += 10000; /* shouldn't take this long! */
+ pthread_mutex_lock(&m1);
+
+ pthread_create(&other, NULL, other_thread, NULL);
+ pthread_yield();
+ while (b.i < b.n) {
+ pthread_cond_signal(&c);
+ pthread_cond_timedwait(&c, &m1, &waketime);
+ }
+ pthread_join(other, NULL);
+ pthread_mutex_unlock(&m1);
+
+ b.divisor = 2;
+ bench_report(&b);
+
+ /* expired test */
+ bench_init(&b, NULL, NULL, "per call when already expired");
+ pthread_mutex_init(&m, NULL);
+ pthread_mutex_lock(&m);
+ timespecclear(&ts);
+ bench_amortize(&b, BENCH_LOOPS) {
+ pthread_cond_timedwait(&c, &m, &ts);
+ }
+ pthread_mutex_unlock(&m);
+ bench_report(&b);
+
+ exit(0);
+}
+
+
diff --git a/lib/libc_r/BENCH/cond_wake.c b/lib/libc_r/BENCH/cond_wake.c
new file mode 100644
index 00000000000..11055baaeed
--- /dev/null
+++ b/lib/libc_r/BENCH/cond_wake.c
@@ -0,0 +1,58 @@
+#include <pthread.h>
+#include <string.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Condition Variable, Wake Up";
+static char doc[] =
+"\tThis is the amount of time from when one thread calls\n"
+"\tpthread_cond_signal() and a thread blocked on that condition\n"
+"\tvariable returns from its pthread_cond_wait() call. The condition\n"
+"\tand its associated mutex should not be used by any other thread.\n"
+"\tMetrics shall be provided for both the case when the\n"
+"\tpthread_cond_signal() call is executed under the associated mutex,\n"
+"\tas well as not under the mutex.";
+
+pthread_mutex_t m1, m2;
+pthread_cond_t c;
+bench_t b;
+
+void *
+other_thread(arg)
+ void *arg;
+{
+
+ pthread_set_name_np(pthread_self(), "oth");
+ pthread_mutex_lock(&m2);
+
+ bench_amortize(&b, BENCH_LOOPS) {
+ pthread_cond_wait(&c, &m2);
+ pthread_cond_signal(&c);
+ }
+ pthread_mutex_unlock(&m2);
+}
+
+int
+main() {
+ pthread_t other;
+ bench_init(&b, name, doc, "per call");
+ b.n = BENCH_LOOPS;
+ bench_header(&b);
+ pthread_cond_init(&c, NULL);
+ pthread_mutex_init(&m1, NULL);
+ pthread_mutex_init(&m2, NULL);
+ pthread_mutex_lock(&m1);
+ pthread_create(&other, NULL, other_thread, NULL);
+
+ pthread_yield();
+ while (b.i < b.n) {
+ pthread_cond_signal(&c);
+ pthread_cond_wait(&c, &m1);
+ }
+
+ b.divisor = 2;
+ bench_report(&b);
+ exit(0);
+}
+
+
diff --git a/lib/libc_r/BENCH/mutex_cont.c b/lib/libc_r/BENCH/mutex_cont.c
new file mode 100644
index 00000000000..de9eecb0bce
--- /dev/null
+++ b/lib/libc_r/BENCH/mutex_cont.c
@@ -0,0 +1,128 @@
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Mutex Lock/Unlock, Contention";
+static char doc[] =
+"\tThis is the time interval between when one thread calls\n"
+"\tpthread_mutex_unlock() and another thread that was blocked\n"
+"\ton pthread_mutex_lock() returns with the lock held.";
+
+/*
+
+The order of locking looks like this:
+
+ A B 1 2 3
+ =============== =============== == == ==
+ lock(2) A
+ yield() A
+ lock(1) A B
+ lock(3) B A B
+ yield() B A B
+ lock(1) Ba A B
+-------
+ unlock(1) a A B
+ lock(2) a Ab B
+ ^ A Ab B
+ unlock(2) A b B
+ lock(3) A b Ba
+ ^ A B Ba
+ unlock(3) A B a
+ lock(1) Ab B a
+ ^ Ab B A
+ unlock(1) b B A
+ lock(2) b Ba A
+ ^ B Ba A
+ unlock(2) B a A
+ lock(3) B a Ab
+ ^ B A Ab
+ unlock(3) B A b
+ lock(1) Ba A b
+ ^ Ba A B
+-------
+ unlock(1) a A B
+ unlock(3) a A
+ exit
+ ^ A A
+ unlock(1) A
+ unlock(2)
+ exit
+
+ In every cycle, there will be 6 transitions and 6 lock/unlock
+ pairs. So, to compute the transition time, we subtract the
+ lock/unlock time computed without contention.
+*/
+
+static pthread_mutex_t m1, m2, m3;
+static bench_t ba, bb;
+
+void *
+thread_a(arg)
+{
+ pthread_set_name_np(pthread_self(), "ta");
+ pthread_mutex_lock(&m2);
+ pthread_yield();
+
+ pthread_mutex_lock(&m1);
+ bench_amortize(&ba, BENCH_LOOPS) {
+ pthread_mutex_unlock(&m2);
+ pthread_mutex_lock(&m3);
+ pthread_mutex_unlock(&m1);
+ pthread_mutex_lock(&m2);
+ pthread_mutex_unlock(&m3);
+ pthread_mutex_lock(&m1);
+ }
+ pthread_mutex_unlock(&m1);
+ pthread_mutex_unlock(&m2);
+ return (NULL);
+}
+
+void *
+thread_b(arg)
+{
+ pthread_set_name_np(pthread_self(), "tb");
+ pthread_mutex_lock(&m1);
+ pthread_mutex_lock(&m3);
+ pthread_yield();
+
+ bench_amortize(&bb, BENCH_LOOPS) {
+ pthread_mutex_unlock(&m1);
+ pthread_mutex_lock(&m2);
+ pthread_mutex_unlock(&m3);
+ pthread_mutex_lock(&m1);
+ pthread_mutex_unlock(&m2);
+ pthread_mutex_lock(&m3);
+ }
+ pthread_mutex_unlock(&m1);
+ pthread_mutex_unlock(&m3);
+ return (NULL);
+}
+
+int
+main() {
+ pthread_t ta, tb;
+
+ bench_init(&ba, name, doc, "from unlock to lock inclusive");
+ bench_init(&bb, NULL, NULL, NULL);
+
+ bench_header(&ba);
+
+ pthread_mutex_init(&m1, NULL);
+ pthread_mutex_init(&m2, NULL);
+ pthread_mutex_init(&m3, NULL);
+
+ pthread_create(&ta, NULL, thread_a, NULL);
+ pthread_create(&tb, NULL, thread_b, NULL);
+
+ pthread_join(ta, NULL);
+ pthread_join(tb, NULL);
+
+ ba.divisor = bb.divisor = 6;
+
+ bench_report(&ba);
+/* bench_report(&bb); */
+ exit(0);
+}
+
diff --git a/lib/libc_r/BENCH/mutex_nocont.c b/lib/libc_r/BENCH/mutex_nocont.c
new file mode 100644
index 00000000000..46f13791296
--- /dev/null
+++ b/lib/libc_r/BENCH/mutex_nocont.c
@@ -0,0 +1,29 @@
+#include <pthread.h>
+#include <string.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Mutex Lock/Unlock, No Contention";
+static char doc[] =
+"\tThis is the time interval needed to call pthread_mutex_lock()\n"
+"\tfollowed immediately by pthread_mutex_unlock() on a mutex that\n"
+"\tis unowned and which is only being used by the thread doing\n"
+"\tthe test.";
+
+int
+main() {
+ pthread_mutex_t m;
+ bench_t b;
+
+ bench_init(&b, name, doc, "from lock to unlock inclusive");
+ bench_header(&b);
+ pthread_mutex_init(&m, NULL);
+ bench_amortize(&b, BENCH_LOOPS) {
+ pthread_mutex_lock(&m);
+ pthread_mutex_unlock(&m);
+ }
+ bench_report(&b);
+ exit(0);
+}
+
+
diff --git a/lib/libc_r/BENCH/null.c b/lib/libc_r/BENCH/null.c
new file mode 100644
index 00000000000..cbd7ad3a4f6
--- /dev/null
+++ b/lib/libc_r/BENCH/null.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <pthread.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Null test";
+static char doc[] =
+"\tThe time needed for performing a tight empty loop. This indicates\n"
+"\tthe overhead incurred by the measurement harness.";
+
+
+
+int
+main() {
+ bench_t b;
+
+ bench_init(&b, name, doc, "per cycle");
+ bench_header(&b);
+ bench_amortize(&b, BENCH_LOOPS) {
+ /* nothng */
+ }
+ bench_report(&b);
+ exit(0);
+}
+
+
diff --git a/lib/libc_r/BENCH/once_overhead.c b/lib/libc_r/BENCH/once_overhead.c
new file mode 100644
index 00000000000..c353ffb8d52
--- /dev/null
+++ b/lib/libc_r/BENCH/once_overhead.c
@@ -0,0 +1,30 @@
+
+#include <pthread.h>
+#include "bench.h"
+
+static char name[] = "Once Overhead";
+static char doc[] =
+"\tThe time needed for the highest priority thread to execute the\n"
+"\tpthread_once() function when the init_routine has already been\n"
+"\texecuted.";
+
+void
+init_routine()
+{
+}
+
+int
+main() {
+ pthread_once_t once_control = PTHREAD_ONCE_INIT;
+ bench_t b;
+ bench_init(&b, name, doc, "per call");
+ bench_header(&b);
+ pthread_once(&once_control, init_routine);
+ bench_amortize(&b, BENCH_LOOPS) {
+ pthread_once(&once_control, init_routine);
+ }
+ bench_report(&b);
+ exit(0);
+}
+
+
diff --git a/lib/libc_r/BENCH/self_overhead.c b/lib/libc_r/BENCH/self_overhead.c
new file mode 100644
index 00000000000..eea74cfc75e
--- /dev/null
+++ b/lib/libc_r/BENCH/self_overhead.c
@@ -0,0 +1,79 @@
+#include <pthread.h>
+#include <string.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Self Overhead";
+static char doc[] =
+"\tThe time needed for the highest priority thread to perform the\n"
+"\tpthread_self() operation, for the following numbers of threads:\n"
+"\t1, 21, 101, 1023";
+
+
+static int nthreads = 1;
+pthread_t children[1024];
+
+void *
+child() {
+ pause();
+}
+
+void
+numthreads(n)
+ int n;
+{
+ int error;
+ pthread_attr_t small_stack_attr;
+
+ pthread_attr_init(&small_stack_attr);
+ pthread_attr_setstacksize(&small_stack_attr, PTHREAD_STACK_MIN);
+
+ while (nthreads < n) {
+ error = pthread_create(&children[nthreads],
+ &small_stack_attr, child, NULL);
+ if (error != 0)
+ errx(1, "pthread_create #%d: %s", nthreads,
+ strerror(error));
+ pthread_yield();
+ nthreads++;
+ }
+
+ while (nthreads > n) {
+ error = pthread_cancel(children[nthreads - 1]);
+ if (error != 0)
+ errx(1, "pthread_cancel: %s", strerror(error));
+ pthread_yield();
+ nthreads --;
+ }
+
+ printf("\n#threads: %d\n", nthreads);
+}
+
+void
+doit(b, n)
+ bench_t *b;
+ int n;
+{
+
+ numthreads(n);
+ bench_amortize(b, BENCH_LOOPS) {
+ pthread_self();
+ }
+ bench_report(b);
+}
+
+int
+main() {
+ bench_t b;
+
+ bench_init(&b, name, doc, "per call");
+ bench_header(&b);
+
+ doit(&b, 1);
+ doit(&b, 21);
+ doit(&b, 101);
+ doit(&b, 1023);
+ exit(0);
+}
+
+
diff --git a/lib/libc_r/BENCH/yield.c b/lib/libc_r/BENCH/yield.c
new file mode 100644
index 00000000000..c197afd2b2d
--- /dev/null
+++ b/lib/libc_r/BENCH/yield.c
@@ -0,0 +1,55 @@
+#include <pthread.h>
+#include <sched.h>
+#include <string.h>
+#include <err.h>
+#include "bench.h"
+
+static char name[] = "Thread Yield Time (Busy)";
+static char doc[] =
+"\tThread yield time is defined as the amount of time between that\n"
+"\tpoint when a running thread voluntarily gives up the CPU until\n"
+"\tthe highest priority runnable thread begins execution of its\n"
+"\tapplication code.";
+
+#ifdef DEBUG
+volatile int state = 0;
+#endif
+bench_t b;
+
+void *
+other_thread(arg)
+ void *arg;
+{
+
+ pthread_set_name_np(pthread_self(), "oth");
+ bench_amortize(&b, BENCH_LOOPS) {
+#ifdef DEBUG
+ if (state != 0) abort();
+ state = 1;
+#endif
+ sched_yield();
+ }
+}
+
+int
+main() {
+ pthread_t other;
+
+ bench_init(&b, name, doc, "per yield");
+ b.n = BENCH_LOOPS;
+ bench_header(&b);
+ pthread_create(&other, NULL, other_thread, NULL);
+ while (b.i < b.n) {
+#ifdef DEBUG
+ if (state != 1) abort();
+ state = 0;
+#endif
+ sched_yield();
+ }
+ pthread_join(other, NULL);
+ b.divisor = 2;
+ bench_report(&b);
+ exit(0);
+}
+
+