From ff93535c71a52b327047819a2aeb5372384f859c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 11 Nov 2011 12:34:03 -0500 Subject: [PATCH] New src/test/bench.c to allow us to actually _run_ benchmark code Yes, the timing functions are suboptimal. Please improve! --- .gitignore | 2 + changes/bench | 6 + configure.in | 5 +- src/test/Makefile.am | 12 +- src/test/bench.c | 254 +++++++++++++++++++++++++++++++++++++++++++ src/test/test.c | 98 ----------------- 6 files changed, 275 insertions(+), 102 deletions(-) create mode 100644 changes/bench create mode 100644 src/test/bench.c diff --git a/.gitignore b/.gitignore index 610965b85..26e9e3808 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,8 @@ # /src/test /src/test/Makefile /src/test/Makefile.in +/src/test/bench +/src/test/bench.exe /src/test/test /src/test/test-child /src/test/test.exe diff --git a/changes/bench b/changes/bench new file mode 100644 index 000000000..4479988e6 --- /dev/null +++ b/changes/bench @@ -0,0 +1,6 @@ + o Testing + - The long-disabled benchmark tests are now split into their own + ./src/test/bench binary. + - The benchmarks can now use more accurate timers than gettimeofday + when such are available. + diff --git a/configure.in b/configure.in index 17f5c8d5e..d13b54d93 100644 --- a/configure.in +++ b/configure.in @@ -276,6 +276,7 @@ AC_SEARCH_LIBS(socket, [socket]) AC_SEARCH_LIBS(gethostbyname, [nsl]) AC_SEARCH_LIBS(dlopen, [dl]) AC_SEARCH_LIBS(inet_aton, [resolv]) +AC_SEARCH_LIBS([clock_gettime], [rt], [have_rt=yes]) if test "$enable_threads" = "yes"; then AC_SEARCH_LIBS(pthread_create, [pthread]) @@ -288,6 +289,7 @@ dnl exports strlcpy without defining it in a header. AC_CHECK_FUNCS( accept4 \ + clock_gettime \ flock \ ftime \ getaddrinfo \ @@ -363,9 +365,6 @@ dnl On Gnu/Linux or any place we require it, we'll add librt to the Libevent dnl linking for static builds. STATIC_LIBEVENT_FLAGS="" if test "$enable_static_libevent" = "yes"; then - dnl Determine if we have clock_gettime in librt - AC_SEARCH_LIBS([clock_gettime], [rt], - [have_rt=yes]) if test "$have_rt" = yes; then STATIC_LIBEVENT_FLAGS=" -lrt " fi diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 301452b4e..ffe1f942e 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -1,6 +1,6 @@ TESTS = test -noinst_PROGRAMS = test test-child +noinst_PROGRAMS = test test-child bench AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ @@ -23,6 +23,9 @@ test_SOURCES = \ test_util.c \ tinytest.c +bench_SOURCES = \ + bench.c + test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ test_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \ @@ -30,6 +33,13 @@ test_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \ @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ +bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ + @TOR_LDFLAGS_libevent@ +bench_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \ + ../common/libor-event.a \ + @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ + noinst_HEADERS = \ tinytest.h \ tinytest_macros.h \ diff --git a/src/test/bench.c b/src/test/bench.c new file mode 100644 index 000000000..1de52ebfc --- /dev/null +++ b/src/test/bench.c @@ -0,0 +1,254 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2011, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Ordinarily defined in tor_main.c; this bit is just here to provide one + * since we're not linking to tor_main.c */ +const char tor_git_revision[] = ""; + +/** + * \file bench.c + * \brief Benchmarks for lower level Tor modules. + **/ + +#include "orconfig.h" + +#include "or.h" + +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) +static uint64_t nanostart; +static inline uint64_t +timespec_to_nsec(const struct timespec *ts) +{ + return ((uint64_t)ts->tv_sec)*1000000000 + ts->tv_nsec; +} + +static void +reset_perftime(void) +{ + struct timespec ts; + int r; + r = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); + tor_assert(r == 0); + nanostart = timespec_to_nsec(&ts); +} + +static uint64_t +perftime(void) +{ + struct timespec ts; + int r; + r = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); + tor_assert(r == 0); + return timespec_to_nsec(&ts) - nanostart; +} + +#else +static struct timeval tv_start = { 0, 0 }; +static void +reset_perftime(void) +{ + tor_gettimeofday(&tv_start); +} +static uint64_t +perftime(void) +{ + struct timeval now, out; + tor_gettimeofday(&now); + timersub(&now, &tv_start, &out); + return ((uint64_t)out.tv_sec)*1000000000 + out.tv_usec*1000; +} +#endif + +/** Run AES performance benchmarks. */ +static void +bench_aes(void) +{ + int len, i; + char *b1, *b2; + crypto_cipher_env_t *c; + uint64_t start, end; + const int bytes_per_iter = (1<<24); + reset_perftime(); + c = crypto_new_cipher_env(); + crypto_cipher_generate_key(c); + crypto_cipher_encrypt_init_cipher(c); + for (len = 1; len <= 8192; len *= 2) { + int iters = bytes_per_iter / len; + b1 = tor_malloc_zero(len); + b2 = tor_malloc_zero(len); + start = perftime(); + for (i = 0; i < iters; ++i) { + crypto_cipher_encrypt(c, b1, b2, len); + } + end = perftime(); + tor_free(b1); + tor_free(b2); + printf("%d bytes: %.2f nsec per byte\n", len, + ((double)(end-start))/(iters*len)); + } + crypto_free_cipher_env(c); +} + +static void +bench_cell_aes(void) +{ + uint64_t start, end; + const int len = 509; + const int iters = (1<<16); + const int max_misalign = 15; + char *b = tor_malloc(len+max_misalign); + crypto_cipher_env_t *c; + int i, misalign; + + c = crypto_new_cipher_env(); + crypto_cipher_generate_key(c); + crypto_cipher_encrypt_init_cipher(c); + + reset_perftime(); + for (misalign = 0; misalign <= max_misalign; ++misalign) { + start = perftime(); + for (i = 0; i < iters; ++i) { + crypto_cipher_crypt_inplace(c, b+misalign, len); + } + end = perftime(); + printf("%d bytes, misaligned by %d: %.2f nsec per byte\n", len, misalign, + ((double)(end-start))/(iters*len)); + } + + crypto_free_cipher_env(c); + tor_free(b); +} + +/** Run digestmap_t performance benchmarks. */ +static void +bench_dmap(void) +{ + smartlist_t *sl = smartlist_create(); + smartlist_t *sl2 = smartlist_create(); + struct timeval start, end, pt2, pt3, pt4; + const int iters = 10000; + const int elts = 4000; + const int fpostests = 1000000; + char d[20]; + int i,n=0, fp = 0; + digestmap_t *dm = digestmap_new(); + digestset_t *ds = digestset_new(elts); + + for (i = 0; i < elts; ++i) { + crypto_rand(d, 20); + smartlist_add(sl, tor_memdup(d, 20)); + } + for (i = 0; i < elts; ++i) { + crypto_rand(d, 20); + smartlist_add(sl2, tor_memdup(d, 20)); + } + printf("nbits=%d\n", ds->mask+1); + + tor_gettimeofday(&start); + for (i = 0; i < iters; ++i) { + SMARTLIST_FOREACH(sl, const char *, cp, digestmap_set(dm, cp, (void*)1)); + } + tor_gettimeofday(&pt2); + for (i = 0; i < iters; ++i) { + SMARTLIST_FOREACH(sl, const char *, cp, digestmap_get(dm, cp)); + SMARTLIST_FOREACH(sl2, const char *, cp, digestmap_get(dm, cp)); + } + tor_gettimeofday(&pt3); + for (i = 0; i < iters; ++i) { + SMARTLIST_FOREACH(sl, const char *, cp, digestset_add(ds, cp)); + } + tor_gettimeofday(&pt4); + for (i = 0; i < iters; ++i) { + SMARTLIST_FOREACH(sl, const char *, cp, n += digestset_isin(ds, cp)); + SMARTLIST_FOREACH(sl2, const char *, cp, n += digestset_isin(ds, cp)); + } + tor_gettimeofday(&end); + + for (i = 0; i < fpostests; ++i) { + crypto_rand(d, 20); + if (digestset_isin(ds, d)) ++fp; + } + + printf("%ld\n",(unsigned long)tv_udiff(&start, &pt2)); + printf("%ld\n",(unsigned long)tv_udiff(&pt2, &pt3)); + printf("%ld\n",(unsigned long)tv_udiff(&pt3, &pt4)); + printf("%ld\n",(unsigned long)tv_udiff(&pt4, &end)); + printf("-- %d\n", n); + printf("++ %f\n", fp/(double)fpostests); + digestmap_free(dm, NULL); + digestset_free(ds); + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(sl2, char *, cp, tor_free(cp)); + smartlist_free(sl); + smartlist_free(sl2); +} + +typedef void (*bench_fn)(void); + +typedef struct benchmark_t { + const char *name; + bench_fn fn; + int enabled; +} benchmark_t; + +#define ENT(s) { #s , bench_##s, 0 } + +static struct benchmark_t benchmarks[] = { + ENT(dmap), + ENT(aes), + ENT(cell_aes), + {NULL,NULL,0} +}; + +static benchmark_t * +find_benchmark(const char *name) +{ + benchmark_t *b; + for (b = benchmarks; b->name; ++b) { + if (!strcmp(name, b->name)) { + return b; + } + } + return NULL; +} + +/** Main entry point for benchmark code: parse the command line, and run + * some benchmarks. */ +int +main(int argc, const char **argv) +{ + int i; + int list=0, n_enabled=0; + benchmark_t *b; + + tor_threads_init(); + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--list")) { + list = 1; + } else { + benchmark_t *b = find_benchmark(argv[i]); + ++n_enabled; + if (b) { + b->enabled = 1; + } else { + printf("No such benchmark as %s\n", argv[i]); + } + } + } + + reset_perftime(); + + for (b = benchmarks; b->name; ++b) { + if (b->enabled || n_enabled == 0) { + printf("===== %s =====\n", b->name); + if (!list) + b->fn(); + } + } + + return 0; +} + diff --git a/src/test/test.c b/src/test/test.c index 185bf5a30..d4edf1484 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1265,102 +1265,6 @@ test_policies(void) } } -/** Run AES performance benchmarks. */ -static void -test_bench_aes(void) -{ - int len, i; - char *b1, *b2; - crypto_cipher_env_t *c; - struct timeval start, end; - const int iters = 100000; - uint64_t nsec; - c = crypto_new_cipher_env(); - crypto_cipher_generate_key(c); - crypto_cipher_encrypt_init_cipher(c); - for (len = 1; len <= 8192; len *= 2) { - b1 = tor_malloc_zero(len); - b2 = tor_malloc_zero(len); - tor_gettimeofday(&start); - for (i = 0; i < iters; ++i) { - crypto_cipher_encrypt(c, b1, b2, len); - } - tor_gettimeofday(&end); - tor_free(b1); - tor_free(b2); - nsec = (uint64_t) tv_udiff(&start,&end); - nsec *= 1000; - nsec /= (iters*len); - printf("%d bytes: "U64_FORMAT" nsec per byte\n", len, - U64_PRINTF_ARG(nsec)); - } - crypto_free_cipher_env(c); -} - -/** Run digestmap_t performance benchmarks. */ -static void -test_bench_dmap(void) -{ - smartlist_t *sl = smartlist_create(); - smartlist_t *sl2 = smartlist_create(); - struct timeval start, end, pt2, pt3, pt4; - const int iters = 10000; - const int elts = 4000; - const int fpostests = 1000000; - char d[20]; - int i,n=0, fp = 0; - digestmap_t *dm = digestmap_new(); - digestset_t *ds = digestset_new(elts); - - for (i = 0; i < elts; ++i) { - crypto_rand(d, 20); - smartlist_add(sl, tor_memdup(d, 20)); - } - for (i = 0; i < elts; ++i) { - crypto_rand(d, 20); - smartlist_add(sl2, tor_memdup(d, 20)); - } - printf("nbits=%d\n", ds->mask+1); - - tor_gettimeofday(&start); - for (i = 0; i < iters; ++i) { - SMARTLIST_FOREACH(sl, const char *, cp, digestmap_set(dm, cp, (void*)1)); - } - tor_gettimeofday(&pt2); - for (i = 0; i < iters; ++i) { - SMARTLIST_FOREACH(sl, const char *, cp, digestmap_get(dm, cp)); - SMARTLIST_FOREACH(sl2, const char *, cp, digestmap_get(dm, cp)); - } - tor_gettimeofday(&pt3); - for (i = 0; i < iters; ++i) { - SMARTLIST_FOREACH(sl, const char *, cp, digestset_add(ds, cp)); - } - tor_gettimeofday(&pt4); - for (i = 0; i < iters; ++i) { - SMARTLIST_FOREACH(sl, const char *, cp, n += digestset_isin(ds, cp)); - SMARTLIST_FOREACH(sl2, const char *, cp, n += digestset_isin(ds, cp)); - } - tor_gettimeofday(&end); - - for (i = 0; i < fpostests; ++i) { - crypto_rand(d, 20); - if (digestset_isin(ds, d)) ++fp; - } - - printf("%ld\n",(unsigned long)tv_udiff(&start, &pt2)); - printf("%ld\n",(unsigned long)tv_udiff(&pt2, &pt3)); - printf("%ld\n",(unsigned long)tv_udiff(&pt3, &pt4)); - printf("%ld\n",(unsigned long)tv_udiff(&pt4, &end)); - printf("-- %d\n", n); - printf("++ %f\n", fp/(double)fpostests); - digestmap_free(dm, NULL); - digestset_free(ds); - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); - SMARTLIST_FOREACH(sl2, char *, cp, tor_free(cp)); - smartlist_free(sl); - smartlist_free(sl2); -} - /** Test encoding and parsing of rendezvous service descriptors. */ static void test_rend_fns(void) @@ -1913,8 +1817,6 @@ static struct testcase_t test_array[] = { ENT(geoip), FORK(stats), - DISABLED(bench_aes), - DISABLED(bench_dmap), END_OF_TESTCASES };