shithub: 9bench

Download patch

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