cbench/cbench.c
apio 1810a0a89b
fix: Open /dev/null only once and close it at the end
Before this patch, we would open and close /dev/null every
iteration.
2023-05-03 17:05:17 +02:00

143 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, int fd)
{
struct timespec start, end;
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);
}
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;
int fd = open("/dev/null", O_WRONLY);
if(fd < 0)
{
perror("open");
return 1;
}
for(int i = 0; i < iterations; i++)
{
uint64_t ns = run_individual_benchmark(argv+1, fd);
if(ns == (uint64_t)-1)
{
close(fd);
return 1;
}
total += ns;
if(ns < min) min = ns;
if(ns > max) max = ns;
}
close(fd);
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);
}