diff options
Diffstat (limited to 'benchmark/benchmark.c')
| -rw-r--r-- | benchmark/benchmark.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/benchmark/benchmark.c b/benchmark/benchmark.c new file mode 100644 index 0000000..2fe3163 --- /dev/null +++ b/benchmark/benchmark.c | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | #define _POSIX_C_SOURCE 199309L | ||
| 2 | #include "benchmark.h" | ||
| 3 | #include "resource_usage.h" | ||
| 4 | #include <stdio.h> | ||
| 5 | |||
| 6 | #if defined(_WIN32) || defined(_WIN64) | ||
| 7 | #include <windows.h> | ||
| 8 | #else | ||
| 9 | #include <time.h> | ||
| 10 | #endif | ||
| 11 | |||
| 12 | /* ANSI color codes */ | ||
| 13 | const char* COLOR_RED = "\033[31m"; | ||
| 14 | const char* COLOR_GREEN = "\033[32m"; | ||
| 15 | const char* COLOR_YELLOW = "\033[33m"; | ||
| 16 | const char* COLOR_RESET = "\033[0m"; | ||
| 17 | |||
| 18 | /* Maximum allowed average execution time per call in milliseconds */ | ||
| 19 | const double MAX_ALLOWED_MS = 0.05; | ||
| 20 | |||
| 21 | /* Default benchmark iteration counts */ | ||
| 22 | const size_t DEFAULT_BENCHMARKS = 100; | ||
| 23 | const size_t ITERATIONS_FAST = 1000; | ||
| 24 | const size_t ITERATIONS_SLOW = 10; | ||
| 25 | |||
| 26 | /* Volatile sink to prevent compiler optimizations removing the function call */ | ||
| 27 | static volatile int sink; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * @brief Cross-platform monotonic time in milliseconds | ||
| 31 | */ | ||
| 32 | static double now_ms(void) | ||
| 33 | { | ||
| 34 | #if defined(_WIN32) || defined(_WIN64) | ||
| 35 | LARGE_INTEGER freq, counter; | ||
| 36 | QueryPerformanceFrequency(&freq); | ||
| 37 | QueryPerformanceCounter(&counter); | ||
| 38 | return (double)counter.QuadPart * 1000.0 / (double)freq.QuadPart; | ||
| 39 | #else | ||
| 40 | struct timespec ts; | ||
| 41 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
| 42 | return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6; | ||
| 43 | #endif | ||
| 44 | } | ||
| 45 | |||
| 46 | double benchmark_func(const function_to_benchmark func, | ||
| 47 | const char* arg, | ||
| 48 | size_t benchmarks) | ||
| 49 | { | ||
| 50 | if (!func) | ||
| 51 | return 0.0; | ||
| 52 | if (benchmarks == 0) | ||
| 53 | benchmarks = DEFAULT_BENCHMARKS; | ||
| 54 | size_t warmup = benchmarks / 10; | ||
| 55 | if (warmup > 1000) | ||
| 56 | warmup = 1000; | ||
| 57 | for (size_t i = 0; i < warmup; ++i) | ||
| 58 | sink = func(arg); | ||
| 59 | const double start = now_ms(); | ||
| 60 | for (size_t i = 0; i < benchmarks; ++i) | ||
| 61 | sink = func(arg); | ||
| 62 | const double end = now_ms(); | ||
| 63 | return (end - start) / benchmarks; | ||
| 64 | } | ||
| 65 | |||
| 66 | void report_result(const char* test_name, | ||
| 67 | const int passed, | ||
| 68 | const double avg_ms, | ||
| 69 | const double max_allowed_ms) | ||
| 70 | { | ||
| 71 | const int is_pass = passed && (avg_ms <= max_allowed_ms); | ||
| 72 | const char* status_color = is_pass ? COLOR_GREEN : COLOR_RED; | ||
| 73 | const char* status_text = is_pass ? "PASS" : "FAIL"; | ||
| 74 | |||
| 75 | if (!is_pass && avg_ms > max_allowed_ms) | ||
| 76 | status_text = "SLOW"; | ||
| 77 | |||
| 78 | printf("[avg %.6f ms] %s%s: %s%s\n", | ||
| 79 | avg_ms, | ||
| 80 | status_color, | ||
| 81 | status_text, | ||
| 82 | test_name, | ||
| 83 | COLOR_RESET); | ||
| 84 | |||
| 85 | if (!is_pass && avg_ms > max_allowed_ms) { | ||
| 86 | printf(" %sReason:%s exceeded %.3f ms limit\n", | ||
| 87 | COLOR_YELLOW, | ||
| 88 | COLOR_RESET, | ||
| 89 | max_allowed_ms); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | void benchmark_func_with_resources(const function_to_benchmark func, | ||
| 94 | const char* arg, | ||
| 95 | const size_t benchmarks, | ||
| 96 | const char* label) | ||
| 97 | { | ||
| 98 | const double avg_ms = benchmark_func(func, arg, benchmarks); | ||
| 99 | printf("[Benchmark] %s avg %.6f ms\n", label, avg_ms); | ||
| 100 | print_resource_usage(label); | ||
| 101 | } | ||
