142 lines
3.3 KiB
C
142 lines
3.3 KiB
C
#define _POSIX_C_SOURCE 199309L
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <sys/wait.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
static inline uint64_t as_nanoseconds(struct timespec* ts) {
|
|
return ts->tv_sec * (uint64_t)1000000000L + ts->tv_nsec;
|
|
}
|
|
|
|
static void as_timespec(uint64_t ns, struct timespec* ts) {
|
|
ts->tv_sec = ns / (uint64_t)1000000000L;
|
|
ts->tv_nsec = ns % (uint64_t)1000000000L;
|
|
}
|
|
|
|
uint64_t run_individual_benchmark(char** argv)
|
|
{
|
|
struct timespec start, end;
|
|
|
|
int fd = open("/dev/null", O_WRONLY);
|
|
if(fd < 0)
|
|
{
|
|
perror("open");
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if(clock_gettime(CLOCK_MONOTONIC, &start) != 0)
|
|
{
|
|
perror("clock_gettime");
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
pid_t child = fork();
|
|
if(child < 0)
|
|
{
|
|
perror("fork");
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if(child == 0)
|
|
{
|
|
dup2(fd, STDOUT_FILENO);
|
|
execvp(argv[0], argv);
|
|
perror("execvp");
|
|
exit(1);
|
|
}
|
|
|
|
close(fd);
|
|
int status;
|
|
pid_t result = waitpid(child, &status, 0);
|
|
|
|
// We fetch the time before checking waitpid's status because we want to have a somewhat accurate result.
|
|
if(clock_gettime(CLOCK_MONOTONIC, &end) != 0)
|
|
{
|
|
perror("clock_gettime");
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if(result != child)
|
|
{
|
|
perror("waitpid");
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
if(WIFEXITED(status) && WEXITSTATUS(status) != 0)
|
|
{
|
|
fprintf(stderr, "Child exited with status %d\n", WEXITSTATUS(status));
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
return as_nanoseconds(&end) - as_nanoseconds(&start);
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
if(argc == 1)
|
|
{
|
|
fprintf(stderr, "Usage: %s [iterations:20] command\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if(!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))
|
|
{
|
|
printf("Usage: %s [iterations:20] command\n"
|
|
"\n"
|
|
"Benchmarks a program by running it <iterations> times and reporting the \n"
|
|
"average, fastest and slowest time it took to run.\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
int iterations = 20;
|
|
|
|
if(isdigit(*argv[1]))
|
|
{
|
|
if(argc == 2)
|
|
{
|
|
fprintf(stderr, "Usage: %s [iterations:20] command\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
iterations = atoi(argv[1]);
|
|
|
|
argv++;
|
|
}
|
|
|
|
uint64_t total = 0;
|
|
uint64_t min = UINT64_MAX;
|
|
uint64_t max = 0;
|
|
|
|
for(int i = 0; i < iterations; i++)
|
|
{
|
|
uint64_t ns = run_individual_benchmark(argv+1);
|
|
if(ns == (uint64_t)-1)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
total += ns;
|
|
if(ns < min) min = ns;
|
|
if(ns > max) max = ns;
|
|
}
|
|
|
|
uint64_t average = total / (uint64_t)iterations;
|
|
|
|
struct timespec tspec;
|
|
as_timespec(total, &tspec);
|
|
|
|
printf("Results (over %d iterations): \n", iterations);
|
|
|
|
printf("Total time: %3ld.%.6lds\n", tspec.tv_sec, tspec.tv_nsec / 1000L);
|
|
as_timespec(average, &tspec);
|
|
printf("Average time: %3ld.%.6lds\n", tspec.tv_sec, tspec.tv_nsec / 1000L);
|
|
as_timespec(min, &tspec);
|
|
printf("Fastest time: %3ld.%.6lds\n", tspec.tv_sec, tspec.tv_nsec / 1000L);
|
|
as_timespec(max, &tspec);
|
|
printf("Slowest time: %3ld.%.6lds\n", tspec.tv_sec, tspec.tv_nsec / 1000L);
|
|
} |