|
| 1 | +/* |
| 2 | + * ----------------------------------------------------------------------------- |
| 3 | + * File: framework.hpp |
| 4 | + * Project: Fossil Logic |
| 5 | + * Description: This file implments the framework for benchmarking. |
| 6 | + * |
| 7 | + * This file is part of the Fossil Logic project, which aims to develop high- |
| 8 | + * performance, cross-platform applications and libraries. The code contained |
| 9 | + * herein is subject to the terms and conditions defined in the project license. |
| 10 | + * |
| 11 | + * Author: Michael Gene Brockus (Dreamer) |
| 12 | + * Date: 07/01/2024 |
| 13 | + * |
| 14 | + * Copyright (C) 2024 Fossil Logic. All rights reserved. |
| 15 | + * ----------------------------------------------------------------------------- |
| 16 | + */ |
| 17 | +#include "fossil/benchmark/benchmark.h" |
| 18 | +#include <stdio.h> |
| 19 | +#include <time.h> |
| 20 | +#include <float.h> |
| 21 | +#include <stdarg.h> |
| 22 | + |
| 23 | +// |
| 24 | +// local types |
| 25 | +// |
| 26 | +static uint64_t start_time; |
| 27 | + |
| 28 | +#if defined(_WIN32) |
| 29 | +static double frequency; // Variable to store the frequency for Windows |
| 30 | +#endif |
| 31 | + |
| 32 | +void fossil_test_start_benchmark(void) { |
| 33 | +#if defined(_WIN32) |
| 34 | + LARGE_INTEGER freq; |
| 35 | + if (!QueryPerformanceFrequency(&freq)) { |
| 36 | + // Handle error |
| 37 | + fprintf(stderr, "Error: QueryPerformanceFrequency failed\n"); |
| 38 | + exit(EXIT_FAILURE); |
| 39 | + } |
| 40 | + frequency = (double)freq.QuadPart; |
| 41 | + if (!QueryPerformanceCounter((LARGE_INTEGER*)&start_time)) { |
| 42 | + // Handle error |
| 43 | + fprintf(stderr, "Error: QueryPerformanceCounter failed\n"); |
| 44 | + exit(EXIT_FAILURE); |
| 45 | + } |
| 46 | +#else |
| 47 | + struct timespec ts; |
| 48 | + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { |
| 49 | + // Handle error |
| 50 | + perror("Error: clock_gettime failed"); |
| 51 | + exit(EXIT_FAILURE); |
| 52 | + } |
| 53 | + start_time = ts.tv_sec * 1e9 + ts.tv_nsec; |
| 54 | +#endif |
| 55 | +} |
| 56 | + |
| 57 | +uint64_t fossil_test_stop_benchmark(void) { |
| 58 | +#if defined(_WIN32) |
| 59 | + LARGE_INTEGER end_time; |
| 60 | + if (!QueryPerformanceCounter(&end_time)) { |
| 61 | + // Handle error |
| 62 | + fprintf(stderr, "Error: QueryPerformanceCounter failed\n"); |
| 63 | + exit(EXIT_FAILURE); |
| 64 | + } |
| 65 | + return (uint64_t)((end_time.QuadPart - start_time) * 1e9 / frequency); |
| 66 | +#else |
| 67 | + struct timespec ts; |
| 68 | + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { |
| 69 | + // Handle error |
| 70 | + perror("Error: clock_gettime failed"); |
| 71 | + exit(EXIT_FAILURE); |
| 72 | + } |
| 73 | + return (uint64_t)((ts.tv_sec * 1e9 + ts.tv_nsec) - start_time); |
| 74 | +#endif |
| 75 | +} |
| 76 | + |
| 77 | +void assume_duration(double expected, double actual, double unit) { |
| 78 | + clock_t end_time = clock(); |
| 79 | + double elapsed_time = (double)(end_time - start_time) / ((double)CLOCKS_PER_SEC / unit); |
| 80 | + if (elapsed_time < expected) { |
| 81 | + printf("Benchmark failed: expected %f, got %f\n", expected, actual); |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +// Marks a test case as timeout with a specified time and prints it to stderr. |
| 86 | +void fossil_test_benchmark(char* duration_type, double expected, double actual) { |
| 87 | + if (strcmp(duration_type, "minutes") == 0) { |
| 88 | + assume_duration(expected, actual, 60.0); |
| 89 | + } else if (strcmp(duration_type, "seconds") == 0) { |
| 90 | + assume_duration(expected, actual, 1.0); |
| 91 | + } else if (strcmp(duration_type, "milliseconds") == 0) { |
| 92 | + assume_duration(expected, actual, 0.001); |
| 93 | + } else if (strcmp(duration_type, "microseconds") == 0) { |
| 94 | + assume_duration(expected, actual, 1e-6); |
| 95 | + } else if (strcmp(duration_type, "nanoseconds") == 0) { |
| 96 | + assume_duration(expected, actual, 1e-9); |
| 97 | + } else if (strcmp(duration_type, "picoseconds") == 0) { |
| 98 | + assume_duration(expected, actual, 1e-12); |
| 99 | + } else if (strcmp(duration_type, "femtoseconds") == 0) { |
| 100 | + assume_duration(expected, actual, 1e-15); |
| 101 | + } else if (strcmp(duration_type, "attoseconds") == 0) { |
| 102 | + assume_duration(expected, actual, 1e-18); |
| 103 | + } else if (strcmp(duration_type, "zeptoseconds") == 0) { |
| 104 | + assume_duration(expected, actual, 1e-21); |
| 105 | + } else if (strcmp(duration_type, "yoctoseconds") == 0) { |
| 106 | + assume_duration(expected, actual, 1e-24); |
| 107 | + } else { |
| 108 | + printf("Unknown option: %s\n", duration_type); |
| 109 | + } |
| 110 | +} // end of func |
| 111 | + |
| 112 | +void fossil_benchmark_init(fossil_benchmark_t* benchmark, const char* name) { |
| 113 | + benchmark->name = name; |
| 114 | + benchmark->num_samples = 0; |
| 115 | + benchmark->total_duration = 0.0; |
| 116 | + benchmark->min_duration = DBL_MAX; |
| 117 | + benchmark->max_duration = 0.0; |
| 118 | + benchmark->running = 0; |
| 119 | +} |
| 120 | + |
| 121 | +void fossil_benchmark_start(fossil_benchmark_t* benchmark) { |
| 122 | + if (!benchmark->running) { |
| 123 | + benchmark->start_time = clock(); |
| 124 | + benchmark->running = 1; |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +void fossil_benchmark_stop(fossil_benchmark_t* benchmark) { |
| 129 | + if (benchmark->running) { |
| 130 | + benchmark->end_time = clock(); |
| 131 | + double elapsed = ((double)(benchmark->end_time - benchmark->start_time)) / CLOCKS_PER_SEC; |
| 132 | + benchmark->total_duration += elapsed; |
| 133 | + if (elapsed < benchmark->min_duration) { |
| 134 | + benchmark->min_duration = elapsed; |
| 135 | + } |
| 136 | + if (elapsed > benchmark->max_duration) { |
| 137 | + benchmark->max_duration = elapsed; |
| 138 | + } |
| 139 | + benchmark->num_samples++; |
| 140 | + benchmark->running = 0; |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +double fossil_benchmark_elapsed_seconds(const fossil_benchmark_t* benchmark) { |
| 145 | + return benchmark->total_duration; |
| 146 | +} |
| 147 | + |
| 148 | +double fossil_benchmark_min_time(const fossil_benchmark_t* benchmark) { |
| 149 | + return benchmark->min_duration; |
| 150 | +} |
| 151 | + |
| 152 | +double fossil_benchmark_max_time(const fossil_benchmark_t* benchmark) { |
| 153 | + return benchmark->max_duration; |
| 154 | +} |
| 155 | + |
| 156 | +double fossil_benchmark_avg_time(const fossil_benchmark_t* benchmark) { |
| 157 | + return benchmark->num_samples > 0 ? benchmark->total_duration / benchmark->num_samples : 0.0; |
| 158 | +} |
| 159 | + |
| 160 | +void fossil_benchmark_reset(fossil_benchmark_t* benchmark) { |
| 161 | + benchmark->num_samples = 0; |
| 162 | + benchmark->total_duration = 0.0; |
| 163 | + benchmark->min_duration = DBL_MAX; |
| 164 | + benchmark->max_duration = 0.0; |
| 165 | +} |
| 166 | + |
| 167 | +void fossil_benchmark_report(const fossil_benchmark_t* benchmark) { |
| 168 | + printf("\033[1;36mBenchmark : %s\n", benchmark->name); |
| 169 | + printf("\033[1;32mTotal Time: %.6f seconds\n", fossil_benchmark_elapsed_seconds(benchmark)); |
| 170 | + printf("\033[1;32mMin Time : %.6f seconds\n", fossil_benchmark_min_time(benchmark)); |
| 171 | + printf("\033[1;32mMax Time : %.6f seconds\n", fossil_benchmark_max_time(benchmark)); |
| 172 | + printf("\033[1;32mAvg Time : %.6f seconds\n", fossil_benchmark_avg_time(benchmark)); |
| 173 | +} |
| 174 | + |
| 175 | +void fossil_scoped_benchmark_init(scoped_benchmark_t* scoped_benchmark, fossil_benchmark_t* benchmark) { |
| 176 | + scoped_benchmark->benchmark = benchmark; |
| 177 | + fossil_benchmark_start(scoped_benchmark->benchmark); |
| 178 | +} |
| 179 | + |
| 180 | +void fossil_scoped_benchmark_destroy(scoped_benchmark_t* scoped_benchmark) { |
| 181 | + fossil_benchmark_stop(scoped_benchmark->benchmark); |
| 182 | +} |
0 commit comments