#define _POSIX_C_SOURCE 199309L #include #include #include #include #include #include #include #include #include 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 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); }