Split platform specific time measurement away
We now have a `stopwatch` to measure time between two moments.
This commit is contained in:
parent
a7a5e8cfad
commit
739f5d6d5c
3 changed files with 98 additions and 49 deletions
|
|
@ -1,19 +1,10 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "hammer.h"
|
#include "hammer.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include "platform.h"
|
||||||
#ifdef __MACH__
|
|
||||||
#include <mach/clock.h>
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __NetBSD__
|
|
||||||
#include <sys/resource.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char* HParserBackendNames[] = {
|
static const char* HParserBackendNames[] = {
|
||||||
"Packrat",
|
"Packrat",
|
||||||
|
|
@ -23,38 +14,6 @@ static const char* HParserBackendNames[] = {
|
||||||
"GLR"
|
"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:
|
Usage:
|
||||||
Create your parser (i.e., const HParser*), and an array of test cases
|
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);
|
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) {
|
HBenchmarkResults *h_benchmark__m(HAllocator* mm__, HParser* parser, HParserTestcase* testcases) {
|
||||||
// For now, just output the results to stderr
|
// For now, just output the results to stderr
|
||||||
HParserTestcase* tc = testcases;
|
HParserTestcase* tc = testcases;
|
||||||
|
|
@ -135,20 +95,16 @@ HBenchmarkResults *h_benchmark__m(HAllocator* mm__, HParser* parser, HParserTest
|
||||||
|
|
||||||
for (tc = testcases; tc->input != NULL; tc++) {
|
for (tc = testcases; tc->input != NULL; tc++) {
|
||||||
// The goal is to run each testcase for at least 50ms each
|
// 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;
|
int count = 1, cur;
|
||||||
struct timespec ts_start, ts_end;
|
|
||||||
int64_t time_diff;
|
int64_t time_diff;
|
||||||
do {
|
do {
|
||||||
count *= 2; // Yes, this means that the first run will run the function twice. This is fine, as we want multiple runs anyway.
|
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++) {
|
for (cur = 0; cur < count; cur++) {
|
||||||
h_parse_result_free(h_parse(parser, tc->input, tc->length));
|
h_parse_result_free(h_parse(parser, tc->input, tc->length));
|
||||||
}
|
}
|
||||||
h_benchmark_clock_gettime(&ts_end);
|
time_diff = h_platform_stopwatch_ns(&stopwatch);
|
||||||
|
|
||||||
// time_diff is in ns
|
|
||||||
time_diff = (ts_end.tv_sec - ts_start.tv_sec) * 1000000000 + (ts_end.tv_nsec - ts_start.tv_nsec);
|
|
||||||
} while (time_diff < 100000000);
|
} while (time_diff < 100000000);
|
||||||
ret->results[backend].cases[cur_case].parse_time = (time_diff / count);
|
ret->results[backend].cases[cur_case].parse_time = (time_diff / count);
|
||||||
ret->results[backend].cases[cur_case].length = tc->length;
|
ret->results[backend].cases[cur_case].length = tc->length;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include "compiler_specifics.h"
|
#include "compiler_specifics.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/* Error Reporting */
|
/* Error Reporting */
|
||||||
|
|
||||||
/* BSD errx function, seen in err.h */
|
/* BSD errx function, seen in err.h */
|
||||||
|
|
@ -15,4 +17,39 @@ H_MSVC_DECLSPEC(noreturn) \
|
||||||
void h_platform_errx(int err, const char* format, ...) \
|
void h_platform_errx(int err, const char* format, ...) \
|
||||||
H_GCC_ATTRIBUTE((noreturn, format (printf,2,3)));
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,64 @@
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <stdarg.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, ...) {
|
void h_platform_errx(int err, const char* format, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
verrx(err, format, ap);
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue