ref: 3f899338e54ae7ed97d980838a0c40e350718405
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Oct 27 20:22:07 EDT 2020
initial commit
--- /dev/null
+++ b/bench.h
@@ -1,0 +1,40 @@
+#pragma lib "libbench.a"
+#pragma src "/sys/src/libbench"
+
+typedef struct BItem BItem;
+typedef struct BResult BResult;
+typedef struct B B;
+
+typedef void (*BFn)(B* b);
+
+// single benchmark function
+struct BItem
+{+ char *name;
+ BFn fn;
+};
+
+// result of benchmarking
+struct BResult
+{+ int N;
+ vlong ns;
+ uvlong cycles;
+};
+
+// type passed to bench functions
+struct B
+{+ int N;
+ vlong start; /* start ns */
+ vlong ns; /* duration */
+ uvlong scycles; /* start cycles */
+ uvlong ecycles; /* end cycles */
+ uvlong bcycles; /* best cycles */
+ BItem item;
+};
+
+// public api
+void benchinit(void);
+void bench(char *name, BFn);
+void benchitems(BItem[], int);
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,29 @@
+</$objtype/mkfile
+
+TARG=sysbench
+OFILES=\
+ run.$O\
+ test.$O\
+
+HFILES=\
+ bench.h\
+
+CLEANFILES=$O.test $O.true
+
+</sys/src/cmd/mkone
+
+%.$O: %.c
+ $CC -pFTVw $stem.c
+
+install:V: $LIB
+ cp bench.h /sys/include/bench.h
+
+bench: $O.out $O.true
+ $O.out
+
+true.$O: true.c
+ $CC $CFLAGS true.c
+
+$O.true: true.$O
+ $LD -o $target $prereq
+
--- /dev/null
+++ b/run.c
@@ -1,0 +1,226 @@
+#include <u.h>
+#include <libc.h>
+#include <bench.h>
+
+#define BENCHTIME (1000000000) /* 1s in ns */
+
+uvlong boverhead;
+
+static int
+min(int x, int y)
+{+ if(x > y) {+ return y;
+ }
+ return x;
+}
+
+static int
+max(int x, int y)
+{+ if(x < y) {+ return y;
+ }
+ return x;
+}
+
+// run the benchmarking function once, looping n times
+static void
+benchrunn(B *b, int n)
+{+
+ b->N = n;
+
+ // reset
+ b->start = nsec();
+ b->ns = 0;
+ cycles(&b->scycles);
+
+ b->item.fn(b);
+
+ // stop
+ cycles(&b->ecycles);
+ b->ns += nsec() - b->start;
+ b->bcycles += b->ecycles - b->scycles - boverhead;
+}
+
+static vlong
+nsperop(B *b)
+{+ if(b->N <= 0)
+ return 0;
+
+ return b->ns / (vlong)b->N;
+}
+
+static uvlong
+cyperop(B *b)
+{+ if(b->N <= 0)
+ return 0;
+
+ return b->bcycles / (uvlong)b->N;
+}
+
+static int
+rounddown10(int n)
+{+ int tens, result, i;
+
+ tens = 0;
+
+ while(n >= 10) {+ n = n / 10;
+ tens++;
+ }
+
+ result = 1;
+
+ for(i = 0; i < tens; i++) {+ result *= 10;
+ }
+
+ return result;
+}
+
+static int
+roundup(int n)
+{+ int base;
+
+ base = rounddown10(n);
+
+ if(n <= base)
+ return base;
+ if(n <= 2*base)
+ return 2*base;
+ if(n <= 5*base)
+ return 5*base;
+
+ return 10*base;
+}
+
+// run the benchmark for one function
+static BResult
+benchrun(B *b)
+{+ int n, last;
+ vlong d;
+ BResult res;
+
+ n = 1;
+
+ benchrunn(b, n);
+
+ d = BENCHTIME;
+
+ while(b->ns < d && n < 1000000000) {+ last = n;
+ if(nsperop(b) == 0) {+ n = 1000000000;
+ } else {+ n = (int) d/nsperop(b);
+ }
+
+ n = max(min(n+n/2, 100*last), last+1);
+
+ n = roundup(n);
+ benchrunn(b, n);
+ }
+
+ res.N = b->N;
+ res.ns = b->ns;
+ res.cycles = b->bcycles;
+
+ return res;
+}
+
+static void
+benchres(BResult *res)
+{+ char nsop[32];
+ char cyop[32];
+ vlong nsperop;
+ uvlong cyperop;
+
+ if(res->N <= 0) {+ nsperop = 0;
+ cyperop = 0;
+ } else {+ nsperop = res->ns / (vlong)res->N;
+ cyperop = res->cycles / (uvlong)res->N;
+ }
+
+ snprint(nsop, sizeof(nsop), "%10lld ns/op", nsperop);
+ snprint(cyop, sizeof(cyop), "%10ulld cy/op", cyperop);
+
+ if(res->N > 0 && nsperop < 100) {+ if(nsperop < 10)
+ snprint(nsop, sizeof(nsop), "%13.2f ns/op", (double)res->ns / (double)res->N);
+ else
+ snprint(nsop, sizeof(nsop), "%12.1f ns/op", (double)res->ns / (double)res->N);
+ }
+
+ if(res->N > 0 && cyperop < 100) {+ if(cyperop < 10)
+ snprint(cyop, sizeof(cyop), "%13.2f cy/op", (double)res->cycles / (double)res->N);
+ else
+ snprint(cyop, sizeof(cyop), "%12.1f cy/op", (double)res->cycles / (double)res->N);
+ }
+
+ print("%10d N\t%s\t%s\n", res->N, nsop, cyop);+}
+
+/*
+ * public api
+*/
+
+// setup. currently only calculates cycles() overhead.
+// not strictly necessary, but will give better cycle counts.
+void
+benchinit(void)
+{+ int at;
+ uvlong a, b;
+
+ /* figure out cycles overhead */
+ boverhead = -1;
+
+ for(at = 0; at < 1000000; at++) {+ cycles(&a);
+ cycles(&b);
+ if(boverhead > b - a)
+ boverhead = b - a;
+ }
+
+}
+
+// bench a single function
+void
+bench(char *name, BFn fn)
+{+ B b;
+ BResult res;
+
+ memset(&b, 0, sizeof(B));
+ memset(&res, 0, sizeof(BResult));
+
+ b.item.name = name;
+ b.item.fn = fn;
+
+ print("%16s\t", name);+ res = benchrun(&b);
+
+ benchres(&res);
+}
+
+// bench an array of functions
+void
+benchitems(BItem items[], int len)
+{+ int i;
+
+ for(i = 0; i < len; i++) {+ bench(items[i].name, items[i].fn);
+ }
+}
--- /dev/null
+++ b/test.c
@@ -1,0 +1,232 @@
+#include <u.h>
+#include <libc.h>
+#include <bench.h>
+
+typedef struct SLock SLock;
+struct SLock {+ long state;
+ long sem;
+};
+
+int casl(long *, long, long);
+
+void
+slock(SLock *s)
+{+ int i;
+
+ for(i = 0; i < 100; i++){+ if(casl(&s->state, 0, 1))
+ return;
+ sleep(0);
+ }
+ if(ainc(&s->state) == 1)
+ return;
+ while(semacquire(&s->sem, 1) == -1)
+ /* retry */;
+}
+
+void
+sunlock(SLock *s)
+{+ if(adec(&s->state) == 0)
+ return;
+ semrelease(&s->sem, 1);
+}
+
+void
+benchmallocfree32(B *b)
+{+ int i;
+
+ for(i = 0; i < b->N; i++) {+ free(malloc(32));
+ }
+}
+
+void
+benchrand(B *b)
+{+ int i;
+
+ for(i = 0; i < b->N; i++) {+ (void)rand();
+ }
+}
+
+void
+benchtruerand(B *b)
+{+ int i;
+
+ for(i = 0; i < b->N; i++) {+ (void)truerand();
+ }
+}
+
+void
+benchinc(B *b)
+{+ int i;
+ long inc;
+
+ inc = 0;
+ for(i = 0; i < b->N; i++) {+ inc++;
+ }
+}
+
+void
+benchainc(B *b)
+{+ int i;
+ long inc;
+
+ for(i = 0; i < b->N; i++) {+ ainc(&inc);
+ }
+}
+
+void
+benchfork(B *b)
+{+ int i;
+
+ for(i = 0; i < b->N; i++){+ if(!rfork(RFPROC|RFMEM))
+ exits(nil);
+ waitpid();
+ }
+}
+void
+benchmfork(B *b)
+{+ int i;
+
+ for(i = 0; i < b->N; i++){+ if(!fork())
+ exits(nil);
+ waitpid();
+ }
+}
+
+
+void
+benchforkexecl(B *b)
+{+ int i;
+
+ for(i = 0; i < b->N; i++){+ switch(fork()){+ case -1:
+ abort();
+ case 0:
+ execl("./6.true", "6.true", nil);+ print("exec: %r");+ abort();
+ default:
+ waitpid();
+ }
+ }
+}
+
+Lock l;
+QLock q;
+SLock s;
+int count;
+
+void
+hammerlock(int n)
+{+ int i;
+
+ for(i = 0; i < n; i++){+ lock(&l);
+ count++;
+ unlock(&l);
+ }
+}
+
+void
+hammerqlock(int n)
+{+ int i;
+
+ for(i = 0; i < n; i++){+ qlock(&q);
+ count++;
+ qunlock(&q);
+ }
+}
+
+void
+hammerslock(int n)
+{+ int i;
+
+ for(i = 0; i < n; i++){+ slock(&s);
+ count++;
+ sunlock(&s);
+ }
+}
+
+void
+lockbench(void (*fn)(int), int nthr, int ninc)
+{+ int i, p;
+
+ for(i = 0; i < nthr; i++){+ if((p = rfork(RFPROC|RFMEM)) == -1)
+ sysfatal("rfork: %r");+ if(p == 0){+ (*fn)(ninc);
+ exits(nil);
+ }
+ }
+ for(i = 0; i < nthr; i++)
+ free(wait());
+}
+
+#define LKB(nthr) \
+ void benchlock##nthr(B *b){lockbench(hammerlock, nthr, b->N);} \+ void benchqlock##nthr(B *b){lockbench(hammerqlock, nthr, b->N);} \+ void benchslock##nthr(B *b){lockbench(hammerslock, nthr, b->N);}+
+LKB(1)
+LKB(4)
+LKB(32)
+LKB(512)
+
+void
+main(void)
+{+ benchinit();
+
+ bench("mallocfree32", benchmallocfree32);+ bench("rand", benchrand);+ bench("truerand", benchtruerand);+ bench("inc", benchinc);+ bench("ainc", benchainc);+ bench("mfork", benchmfork);+ bench("fork", benchfork);+ bench("forkexecl", benchforkexecl);+
+ bench("lock1", benchlock1);+ bench("qlock1", benchqlock1);+ bench("slock1", benchslock1);+
+ bench("lock4", benchlock4);+ bench("qlock4", benchqlock4);+ bench("slock4", benchslock4);+
+ bench("lock32", benchlock32);+ bench("qlock32", benchqlock32);+ bench("slock32", benchslock32);+
+ bench("lock512", benchlock512);+ bench("qlock512", benchqlock512);+ bench("slock512", benchslock512);+ exits(0);
+}
+
--- /dev/null
+++ b/true.c
@@ -1,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(void)
+{+ exits(0);
+}
+
--
⑨