#ifdef __MACH__
#include <mach/mach.h>
#include <mach/clock.h>
#else
#include <sys/time.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/errno.h>
#include <time.h>
@"imports"@
int reset();
@"declarations"@
int get_elapsed(unsigned long* elapsed) {
/* Store a static timespec as the basis time */
static struct timespec* since = NULL;
struct timespec now;
unsigned long elapsed_value;
/*
* macOS does implement clock_gettime() since 10.12, but, despite
* returning timespec.tv_nsec, it only has usec precision. The following
* returns nanosecond-precise timestamps for all versions of macOS.
*
* Everywhere else, just use clock_gettime().
*/
#ifdef __MACH__
clock_serv_t clock_service;
mach_timespec_t mach_time;
if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clock_service) != 0) {
return 1;
}
if (clock_get_time(clock_service, &mach_time) != 0) {
return 1;
}
if (mach_port_deallocate(mach_task_self(), clock_service) != 0) {
return 1;
}
now.tv_sec = mach_time.tv_sec;
now.tv_nsec = mach_time.tv_nsec;
#else
if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
return 1;
}
#endif
/*
* Uninitialized since means this function has never been called and no basis has been
* established. This malloc will only be called once and never freed; that's okay because
* static since should live for the lifetime of the execution. If since has already been
* initialized, then use it to calculate elapsed time.
*/
if (NULL == since) {
since = malloc(sizeof(struct timespec));
*since = now;
elapsed_value = 0;
} else {
elapsed_value = (now.tv_sec - since->tv_sec)*1e9 + (now.tv_nsec - since->tv_nsec);
}
/*
* If the caller provided a pointer to receive the elapsed value, then set it,
* otherwise reset the basis.
*/
if (NULL != elapsed)
*elapsed = elapsed_value;
else
*since = now;
return 0;
}
int reset() {
return get_elapsed(NULL);
}
int main()
{
static void (*volatile bench_func)(void);
unsigned long i, n_repeat, elapsed;
// A base-10, unsigned, 64-bit integer can be 20 ASCII digits long
char line[21];
int line_len;
// Every benchmark timing must be flushed immediately
setlinebuf(stdout);
@"global"@
while (fgets(line, sizeof line, stdin) != NULL) {
line_len = strlen(line);
if (line_len == 20 && line[19] != '\n') {
fprintf(stderr, "error - input line was too long\n");
return 1;
}
// Strip the newline
line[line_len-1] = '\0';
n_repeat = strtoul(line, NULL, 10);
if (errno) {
fprintf(stderr, "error - '%s' is not an integer\n", line);
return 1;
}
if (n_repeat == 0) {
printf("0 nsec\n");
return 0;
}
@"sample"@
/*
* Use a volatile asm statement inside the loop so
* the compiler will never optimize the number
* of repetitions away. Optimization can still
* happen inside the timed code, but it **WILL**
* get called n_repeat times.
*/
reset();
for (i = 0; i < n_repeat; i++) {
asm volatile("" : "+g" (i));
@"timed"@
}
get_elapsed(&elapsed);
printf("%lu nsec\n", elapsed);
}
if (ferror(stdin)) {
fprintf(stderr, "error reading stdin: %d", ferror(stdin));
return 1;
}
return 0;
}