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);
+}
+