/* * micro_benchmark - simple CLI tool to emulate clocksource switch latency patterns. * It runs configurable loops, estimates timing statistics, and logs results to a file. */ #include #include #include #include struct benchmark_result { int run_count; double average_offset; double std_deviation; }; static struct benchmark_result g_result = {0}; static int g_last_c_state = 0; static int g_last_governor = 0; static double timespec_diff(const struct timespec *start, const struct timespec *end) { time_t sec = end->tv_sec - start->tv_sec; long nsec = end->tv_nsec - start->tv_nsec; return (double)sec + (double)nsec / 1e9; } static void simulate_switch_latency(int c_state, int governor) { volatile double sink = 0.0; unsigned int base_loops = 25000; unsigned int c_factor = (unsigned int)((c_state >= 0 ? c_state : 0) + 1) * 12000U; unsigned int g_factor = (unsigned int)((governor >= 0 ? governor : 0) + 1) * 9000U; unsigned int loops = base_loops + c_factor + g_factor; for (unsigned int i = 0; i < loops; ++i) { sink += (i & 0x7U) * 0.0001; } } double run_benchmark(int runs, int c_state, int governor) { if (runs <= 0) { g_result.run_count = 0; g_result.average_offset = 0.0; g_result.std_deviation = 0.0; return 0.0; } double *samples = malloc(sizeof(double) * (size_t)runs); if (!samples) { fprintf(stderr, "Allocation failed\n"); exit(EXIT_FAILURE); } struct timespec seed_ts; clock_gettime(CLOCK_REALTIME, &seed_ts); srand((unsigned int)seed_ts.tv_nsec); for (int i = 0; i < runs; ++i) { struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); simulate_switch_latency(c_state, governor); clock_gettime(CLOCK_MONOTONIC, &end); double delta = timespec_diff(&start, &end); double jitter = ((double)rand() / (double)RAND_MAX - 0.5) * 5e-6 * (c_state + 1); delta += jitter; if (delta < 0.0) { delta = 0.0; } samples[i] = delta; } double sum = 0.0; for (int i = 0; i < runs; ++i) { sum += samples[i]; } double avg = sum / (double)runs; double variance = 0.0; for (int i = 0; i < runs; ++i) { double diff = samples[i] - avg; variance += diff * diff; } variance /= (double)runs; g_result.run_count = runs; g_result.average_offset = avg; g_result.std_deviation = sqrt(variance); free(samples); return g_result.average_offset; } void log_results(const char *filename) { if (!filename) { return; } FILE *fp = fopen(filename, "w"); if (!fp) { fprintf(stderr, "Could not open log file %s\n", filename); return; } time_t now = time(NULL); fprintf(fp, "# micro_benchmark log\n"); fprintf(fp, "timestamp=%ld\n", (long)now); fprintf(fp, "runs=%d\n", g_result.run_count); fprintf(fp, "c_state=%d\n", g_last_c_state); fprintf(fp, "governor=%d\n", g_last_governor); fprintf(fp, "average_offset=%.9f\n", g_result.average_offset); fprintf(fp, "std_deviation=%.9f\n", g_result.std_deviation); fclose(fp); } int main(int argc, char **argv) { if (argc < 5) { fprintf(stderr, "Usage: %s \n", argv[0]); return EXIT_FAILURE; } int runs = atoi(argv[1]); int c_state = atoi(argv[2]); int governor = atoi(argv[3]); const char *logfile = argv[4]; if (runs <= 0) { fprintf(stderr, "Runs must be positive\n"); return EXIT_FAILURE; } g_last_c_state = c_state; g_last_governor = governor; run_benchmark(runs, c_state, governor); printf("Runs: %d\n", g_result.run_count); printf("C-State: %d\n", g_last_c_state); printf("Governor: %d\n", g_last_governor); printf("Average offset: %.9f s\n", g_result.average_offset); printf("Std dev: %.9f s\n", g_result.std_deviation); log_results(logfile); return EXIT_SUCCESS; }