benchmark.c

Basic benchmarking library in C
git clone git://git.finwo.net/lib/benchmark.c
Log | Files | Refs | README | LICENSE

commit 1b3e1792d499b39195b28f668d011b6148d5889b
parent 2e873d657915a209edf8c0518cea1503adbb106f
Author: finwo <finwo@pm.me>
Date:   Sat, 25 Mar 2023 00:06:41 +0100

Import from mindex library

Diffstat:
M.gitignore | 2++
AMakefile | 29+++++++++++++++++++++++++++++
A__DIRNAME | 2++
Aconfig.mk | 1+
Apackage.ini | 6++++++
Asrc/benchmark.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/benchmark.h | 17+++++++++++++++++
Atest.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 244 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +/test +*.o diff --git a/Makefile b/Makefile @@ -0,0 +1,29 @@ +SRC:= +include config.mk + +INCLUDES?= +INCLUDES+=-I src + +# override CFLAGS?=-Wall -std=c99 +override CFLAGS?=-Wall + +# include lib/.dep/config.mk + +override CFLAGS+=$(INCLUDES) +# override CFLAGS+=-D_DEFAULT_SOURCE + +OBJ=$(SRC:.c=.o) + +BIN=\ + test + +default: $(BIN) + +$(BIN): $(OBJ) $(BIN:=.o) + $(CC) $(CFLAGS) $(OBJ) $@.o -o $@ + +.PHONY: clean +clean: + rm -f $(OBJ) + rm -f $(BIN:=.o) + rm -f test diff --git a/__DIRNAME b/__DIRNAME @@ -0,0 +1 @@ +. +\ No newline at end of file diff --git a/config.mk b/config.mk @@ -0,0 +1 @@ +SRC+=__DIRNAME/src/benchmark.c diff --git a/package.ini b/package.ini @@ -0,0 +1,6 @@ +[export] +config.mk=config.mk +include/finwo/benchmark.h=src/benchmark.h +[package] +deps=lib +name=finwo/benchmark diff --git a/src/benchmark.c b/src/benchmark.c @@ -0,0 +1,132 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> + +#include "benchmark.h" + +struct bmark_queue_t { + void *next; + char *name; + void (*fn)(); +}; +struct bmark_ptile_t { + char n; +}; + +struct bmark_queue_t *bmark_queue = NULL; + +struct bmark_unit_entry { + char *name; + int next; +}; + +struct bmark_unit_entry units[] = { + { "us", 1000 }, + { "ms", 1000 }, + { "s" , 60 }, + { "m" , 0 }, +}; + +const char *header_description = "Description"; + +void bmark_enqueue(char *name, void (*fn)()) { + struct bmark_queue_t *q = calloc(1, sizeof(struct bmark_queue_t)); + q->name = name; + q->fn = fn; + q->next = bmark_queue; + bmark_queue = q; +} + +// Example time numbers: 12.23 s +// 900.00 ms +// 123.45 us +int bmark_run(int run_count, char percentiles[]) { + int header_desclen = strlen(header_description); + int i, r, unit; + struct timeval tv_start; + struct timeval tv_end; + + uint64_t *runs = calloc(run_count, sizeof(uint64_t)); + uint64_t run_tmp = 0; + double run_f = 0; + + // Get max description length + struct bmark_queue_t *q_entry = bmark_queue; + while(q_entry) { + header_desclen = MAX(header_desclen, strlen(q_entry->name)); + q_entry = q_entry->next; + } + + printf("\n"); + + // Separator + printf("+-"); + for(i = 0 ; i < header_desclen ; i++) printf("-"); + for(i = 0 ; percentiles[i] ; i++) printf("-+----------"); + printf("-+\n"); + + // Column names + printf("| %-*s |", header_desclen, header_description); + for(i = 0 ; percentiles[i] ; i++) printf(" %*d %% |", 7, percentiles[i]); + printf("\n"); + + // Separator + printf("+-"); + for(i = 0 ; i < header_desclen ; i++) printf("-"); + for(i = 0 ; percentiles[i] ; i++) printf("-+----------"); + printf("-+\n"); + + // Go over the whole queue + q_entry = bmark_queue; + while(q_entry) { + printf("| %-*s |", header_desclen, q_entry->name); + fflush(stdout); + + // Do the actual runs + for(r = 0; r < run_count ; r++) { + gettimeofday(&tv_start, NULL); + q_entry->fn(); + gettimeofday(&tv_end, NULL); + runs[r] = (tv_end.tv_sec*(uint64_t)1000000+tv_end.tv_usec) - (tv_start.tv_sec*(uint64_t)1000000+tv_start.tv_usec); + } + + // Sort the runs + // Basic bubble sort is fine here, we're not going for a speed record + for(r = 0; r < (run_count-1) ; r++) { + if (r < 0) continue; + if (runs[r] > runs[r+1]) { + run_tmp = runs[r]; + runs[r] = runs[r+1]; + runs[r+1] = run_tmp; + r -= 2; + } + } + + // Print percentiles + for(i = 0 ; percentiles[i] ; i++) { + r = percentiles[i] * (run_count-1) / 100; + run_f = (double)runs[r]; + unit = 0; + while((run_f > units[unit].next) && units[unit].next) { + run_f = run_f / units[unit].next; + unit++; + } + printf(" %*.2lf %2s |", 6, run_f, units[unit].name); + } + + printf("\n"); + + q_entry = q_entry->next; + } + + // Separator + printf("+-"); + for(i = 0 ; i < header_desclen ; i++) printf("-"); + for(i = 0 ; percentiles[i] ; i++) printf("-+----------"); + printf("-+\n"); + + printf("\n"); + return 0; +} diff --git a/src/benchmark.h b/src/benchmark.h @@ -0,0 +1,17 @@ +#ifndef __FINWO_BENCHMARK_H__ +#define __FINWO_BENCHMARK_H__ + +#ifndef MIN +#define MIN(a,b) ((a<b)?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) ((a>b)?(a):(b)) +#endif + +#define BMARK(fn) bmark_enqueue((#fn), (fn)) + +void bmark_enqueue(char *name, void (*fn)()); +int bmark_run(int run_count, char percentiles[]); + +#endif // __FINWO_BENCHMARK_H__ diff --git a/test.c b/test.c @@ -0,0 +1,55 @@ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "benchmark.h" + +/* usleep(): Sleep for the requested number of microseconds. */ +int musleep(long usec) { + struct timespec ts; + int res; + + if (usec < 0) { + return -1; + } + + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + + do { + res = nanosleep(&ts, &ts); + } while (res && errno == EINTR); + + return res; +} + + +void mindex_some_bmark_method() { + // Intentionally empty + musleep(100 + (rand() % 100)); +} + +int main() { + + // Seed random + unsigned int seed; + FILE* urandom = fopen("/dev/urandom", "r"); + fread(&seed, sizeof(int), 1, urandom); + fclose(urandom); + srand(seed); + + // Enqueue benchmarks + BMARK(mindex_some_bmark_method); + + char percentiles[] = { + 1 , 5, 20, + 50, + 80, 95, 99, + 0 + }; + + // Run & return + return bmark_run(100, percentiles); +}