diff --git a/src/benchmark.c b/src/benchmark.c index 8105df3f4f60ff2af0e7640c2472e261c3b6766d..4d7c82503ca7728534ba86cc4c27f5b13cdf4dc0 100644 --- a/src/benchmark.c +++ b/src/benchmark.c @@ -1,19 +1,10 @@ #include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <time.h> #include <string.h> #include "hammer.h" #include "internal.h" - -#ifdef __MACH__ -#include <mach/clock.h> -#include <mach/mach.h> -#endif - -#ifdef __NetBSD__ -#include <sys/resource.h> -#endif +#include "platform.h" static const char* HParserBackendNames[] = { "Packrat", @@ -23,38 +14,6 @@ static const char* HParserBackendNames[] = { "GLR" }; -void h_benchmark_clock_gettime(struct timespec *ts) { - if (ts == NULL) - return; -#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time - /* - * This returns real time, not CPU time. See http://stackoverflow.com/a/6725161 - * Possible solution: http://stackoverflow.com/a/11659289 - */ - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - ts->tv_sec = mts.tv_sec; - ts->tv_nsec = mts.tv_nsec; -#elif defined(__NetBSD__) - // NetBSD doesn't have CLOCK_THREAD_CPUTIME_ID. We'll use getrusage instead - struct rusage rusage; - getrusage(RUSAGE_SELF, &rusage); - ts->tv_nsec = (rusage.ru_utime.tv_usec + rusage.ru_stime.tv_usec) * 1000; - // not going to overflow; can be at most 2e9-2 - ts->tv_sec = rusage.ru_utime.tv_sec + rusage.ru_utime.tv_sec; - if (ts->tv_nsec >= 1000000000) { - ts->tv_nsec -= 1000000000; // subtract a second - ts->tv_sec += 1; // add it back. - } - assert (ts->tv_nsec <= 1000000000); -#else - clock_gettime(CLOCK_THREAD_CPUTIME_ID, ts); -#endif -} - /* Usage: Create your parser (i.e., const HParser*), and an array of test cases @@ -76,6 +35,7 @@ HBenchmarkResults *h_benchmark(HParser* parser, HParserTestcase* testcases) { return h_benchmark__m(&system_allocator, parser, testcases); } +//TODO(uucidl): replace tabs with the right number of spaces HBenchmarkResults *h_benchmark__m(HAllocator* mm__, HParser* parser, HParserTestcase* testcases) { // For now, just output the results to stderr HParserTestcase* tc = testcases; @@ -135,20 +95,16 @@ HBenchmarkResults *h_benchmark__m(HAllocator* mm__, HParser* parser, HParserTest for (tc = testcases; tc->input != NULL; tc++) { // The goal is to run each testcase for at least 50ms each - // TODO: replace this with a posix timer-based benchmark. (cf. timerfd_create, timer_create, setitimer) int count = 1, cur; - struct timespec ts_start, ts_end; int64_t time_diff; do { count *= 2; // Yes, this means that the first run will run the function twice. This is fine, as we want multiple runs anyway. - h_benchmark_clock_gettime(&ts_start); + struct HStopWatch stopwatch; + h_platform_stopwatch_reset(&stopwatch); for (cur = 0; cur < count; cur++) { h_parse_result_free(h_parse(parser, tc->input, tc->length)); } - h_benchmark_clock_gettime(&ts_end); - - // time_diff is in ns - time_diff = (ts_end.tv_sec - ts_start.tv_sec) * 1000000000 + (ts_end.tv_nsec - ts_start.tv_nsec); + time_diff = h_platform_stopwatch_ns(&stopwatch); } while (time_diff < 100000000); ret->results[backend].cases[cur_case].parse_time = (time_diff / count); ret->results[backend].cases[cur_case].length = tc->length; diff --git a/src/platform.h b/src/platform.h index 0c05bfe2e7ada4e7f1c5f48c3d705a839a78730a..21760dd9322d3dc60cc66683a53d17a42cda58f0 100644 --- a/src/platform.h +++ b/src/platform.h @@ -8,6 +8,8 @@ #include "compiler_specifics.h" +#include <stdint.h> + /* Error Reporting */ /* BSD errx function, seen in err.h */ @@ -15,4 +17,39 @@ H_MSVC_DECLSPEC(noreturn) \ void h_platform_errx(int err, const char* format, ...) \ H_GCC_ATTRIBUTE((noreturn, format (printf,2,3))); +/* Time Measurement */ + +struct HStopWatch; /* forward definition */ + +/* initialize a stopwatch */ +void h_platform_stopwatch_reset(struct HStopWatch* stopwatch); + +/* return difference between last reset point and now */ +int64_t h_platform_stopwatch_ns(struct HStopWatch* stopwatch); + +/* Platform dependent definitions for HStopWatch */ +#if defined(_MSC_VER) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +struct HStopWatch { + LARGE_INTEGER qpf; + LARGE_INTEGER start; +}; + +#else +/* Unix like platforms */ + +#include <time.h> + +struct HStopWatch { + struct timespec start; +}; + +#endif + #endif diff --git a/src/platform_bsdlike.c b/src/platform_bsdlike.c index ebb38d9df55243c75774dbfd9789932ee8d9c650..7d892cf9c7c8a35dc795447f0d98664a997a51c6 100644 --- a/src/platform_bsdlike.c +++ b/src/platform_bsdlike.c @@ -3,8 +3,64 @@ #include <err.h> #include <stdarg.h> +#ifdef __MACH__ +#include <mach/clock.h> +#include <mach/mach.h> +#endif + +#ifdef __NetBSD__ +#include <sys/resource.h> +#endif + void h_platform_errx(int err, const char* format, ...) { va_list ap; va_start(ap, format); verrx(err, format, ap); } + +// TODO: replace this with a posix timer-based benchmark. (cf. timerfd_create, timer_create, setitimer) + +static void gettime(struct timespec *ts) { + if (ts == NULL) + return; +#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time + /* + * This returns real time, not CPU time. See http://stackoverflow.com/a/6725161 + * Possible solution: http://stackoverflow.com/a/11659289 + */ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; +#elif defined(__NetBSD__) + // NetBSD doesn't have CLOCK_THREAD_CPUTIME_ID. We'll use getrusage instead + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); + ts->tv_nsec = (rusage.ru_utime.tv_usec + rusage.ru_stime.tv_usec) * 1000; + // not going to overflow; can be at most 2e9-2 + ts->tv_sec = rusage.ru_utime.tv_sec + rusage.ru_utime.tv_sec; + if (ts->tv_nsec >= 1000000000) { + ts->tv_nsec -= 1000000000; // subtract a second + ts->tv_sec += 1; // add it back. + } + assert (ts->tv_nsec <= 1000000000); +#else + clock_gettime(CLOCK_THREAD_CPUTIME_ID, ts); +#endif +} + +void h_platform_stopwatch_reset(struct HStopWatch* stopwatch) { + gettime(&stopwatch->start); +} + +int64_t h_platform_stopwatch_ns(struct HStopWatch* stopwatch) { + struct timespec ts_now; + gettime(&ts_now); + + // time_diff is in ns + return (ts_now.tv_sec - stopwatch->start.tv_sec) * 1000000000 + + (ts_now.tv_nsec - stopwatch->start.tv_nsec); +}