shithub: bench9

ref: 6ae1a31f6bf06c488ff6d1bdf7af04e961c76225
dir: /b.c/

View raw version
#include <u.h>
#include <libc.h>
#include <tos.h>
#include "b.h"

#define MAX(a,b) ((a)>(b)?(a):(b))

enum {
	OneS = 1000000000ULL,
	Btime = OneS/10ULL,
	Bstepmin = 100,
};

static uvlong adj;

typedef struct Res {
	ulong ax, bx, cx, dx;
}Res;

Res cpuid(ulong ax, ulong cx);

void _tend(uvlong *c);
void _tendp(uvlong *c);
void (*tend)(uvlong *c);

uvlong
cycles2ns(uvlong x)
{
	uvlong div;

	/* this is ugly */
	for(div = OneS; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);

	return x / (_tos->cyclefreq / div);
}

static int
σfmt(Fmt *f)
{
	B *b;

	b = va_arg(f->args, B*);

	return fmtprint(f, "%ulld", OneS/(b->tot.ns/b->ic));
}

static int
τfmt(Fmt *f)
{
	Cns c;

	c = va_arg(f->args, Cns);

	return fmtprint(f,	"%ulld", c.ns);
}

B *
benchinit(B *b, char *name)
{
	Res r;
	int i;

	fmtinstall(L'σ', σfmt);
	fmtinstall(L'τ', τfmt);

	if(tend == nil){
		r = cpuid(0x80000001, 2);
		tend = (r.dx & (1<<27)) != 0 ? _tendp : _tend;
	}

	memset(b, 0, sizeof(*b));
	b->name = name;
	b->n = b->nc = Bstepmin;
	b->min.c--;
	b->c = mallocz(b->nc * sizeof(*b->c), 1);

	if(adj == 0){
		uvlong v;
		for(i = 0, v = 0; i < 100; i++){
			uvlong s, e;
			tstart(&s);
			tend(&e);
			v += e - s;
		}
		adj = v / i;
	}

	return b;
}

B *
benchadd(Bgr *gr, char *name)
{
	B **gb, *b;

	if((gb = realloc(gr->b, (gr->nb+1)*sizeof(b))) == nil)
		return nil;
	gr->b = gb;
	if((b = malloc(sizeof(*b)+strlen(name)+1)) == nil)
		return nil;
	gr->b[gr->nb++] = b;

	return benchinit(b, strcpy((char*)(b+1), name));
}

Bgr *
benchinitgr(Bgr *gr, char *name)
{
	gr->name = name;
	gr->b = nil;
	gr->nb = 0;

	return gr;
}

void
benchprintgr(Bgr *gr, int fd)
{
	fprint(fd, "%s\n", gr->name);
	benchprint(gr->b, gr->nb, fd);
}

void
benchfreegr(Bgr *gr)
{
	int i;

	for(i = 0; i < gr->nb; i++)
		free(gr->b[i]);
}

void
bseparator(int fd)
{
	fprint(fd, "\n");
}

static int
ccmp(void *a, void *b)
{
	u32int *x, *y;

	x = a;
	y = b;

	return *x < *y ? -1 : (*x > *y ? 1 : 0);
}

static void
benchcalc(B *b)
{
	uvlong m;
	int i, n;

	qsort(b->c, b->ic, sizeof(*b->c), ccmp);

	if(b->ic & 1)
		m = b->c[b->ic/2];
	else
		m = (b->c[b->ic/2-1] + b->c[b->ic/2])/2;
	b->med.c = m;
	b->med.ns = cycles2ns(b->med.c);
	b->avg.c = b->tot.c / b->step;
	b->avg.ns = cycles2ns(b->avg.c);
	b->min.c = b->c[0];
	b->min.ns = cycles2ns(b->min.c);
	b->max.c = b->c[b->ic-1];
	b->max.ns = cycles2ns(b->max.c);
	for(i = 1; i < 100; i++){
		n = MAX(0, b->ic*i/100 - 1);
		b->p[i].ns = cycles2ns(b->c[n]);
	}
}

void
benchstep(B *b)
{
	uvlong c;

	if(b->n < 1)
		return;

	if(b->step == 0)
		b->t0 = b->tin;
	b->step++;
	b->n--;

	if(b->tout <= b->tin)
		sysfatal("%ulld ≤ %ulld → t₁ ≤ t₀", b->tout, b->tin);
	if(b->tout - b->tin < adj) /* sometimes this happens */
		adj = b->tout - b->tin;
	c = b->tout - b->tin - adj;
	if(b->ic >= b->nc){
		b->nc *= 2;
		b->c = realloc(b->c, b->nc * sizeof(*b->c));
		memset(b->c+b->ic, 0, sizeof(*b->c)*(b->nc - b->ic));
	}
	b->c[b->ic++] = c;

	b->tot.c += c;
	b->tot.ns = cycles2ns(b->tot.c);

	if(b->n == 0){
		uvlong nsall = cycles2ns(b->tout - b->t0);
		if(nsall < Btime)
			b->n = (Btime - nsall) / (nsall / b->step);
		if(b->n == 0){
			benchcalc(b);
			free(b->c);
		}
	}
}

void
benchprint(B **b, int nb, int fd)
{
	static char *header[] = {"   ", "op/s", "98%", "96%", "75%", "med", "avg", "min", "max"};
	static int w[] = {16, 8, 8, 8, 8, 8, 8, 8, 8};
	static int off[] = {
		0,
		0,
		offsetof(B, p[99]),
		offsetof(B, p[95]),
		offsetof(B, p[75]),
		offsetof(B, med),
		offsetof(B, avg),
		offsetof(B, min),
		offsetof(B, max),
	};
	char t[64], *s, *p;
	int i, j, n, x;

	for(i = 0; i < nb; i++){
		w[0] = MAX(w[0], snprint(t, sizeof(t), "%s  ", b[i]->name ? b[i]->name : ""));
		w[1] = MAX(w[1], snprint(t, sizeof(t), "%σ  ", b[i]));
		for(j = 2; j < nelem(header); j++)
			w[j] = MAX(w[j], snprint(t, sizeof(t), "%τ  ", *(Cns*)((char*)b[i] + off[j])));
	}

	for(j = n = 0; j < nelem(header); j++)
		n += w[j];
	s = malloc(n+1);
	memset(s, ' ', n);
	for(j = 0, p = s; j < nelem(header); p += w[j++]){
		x = snprint(t, sizeof(t), "%s  ", header[j]);
		memmove(p+w[j]-x, t, x);
	}
	*p = 0;
	fprint(fd, "%s\n", s);

	for(i = 0; i < nb; i++){
		memset(s, ' ', n);
		p = s;
		p[sprint(p, "%s  ", b[i]->name)] = ' ';
		p += w[0];
		x = snprint(t, sizeof(t), "%σ  ", b[i]);
		memmove(p+w[1]-x, t, x);
		p += w[1];
		for(j = 2; j < nelem(header); p += w[j], j++){
			x = snprint(t, sizeof(t), "%τ  ", *(Cns*)((char*)b[i] + off[j]));
			memmove(p+w[j]-x, t, x);
		}
		*p = 0;
		fprint(fd, "%s\n", s);
	}
}

int
benchwire(int cpu)
{
	char t[64];
	int r, f;

	r = -1;
	snprint(t, sizeof(t), "/proc/%d/ctl", getpid());
	if((f = open(t, OWRITE)) >= 0){
		if(fprint(f, "wired %d\n", cpu) >= 8)
			r = 0;
		close(f);
	}

	return r;
}