diff options
| author | Filip Wandzio <contact@philw.dev> | 2026-03-01 01:03:39 +0100 |
|---|---|---|
| committer | Filip Wandzio <contact@philw.dev> | 2026-03-01 01:03:39 +0100 |
| commit | bf0d77d7d448e964e9716d5af67c48f3d014f090 (patch) | |
| tree | e55f1e91a8c20cd737dfb01dc12a954c25711e01 /benchmark | |
| download | embedded_guardian-bf0d77d7d448e964e9716d5af67c48f3d014f090.tar.gz embedded_guardian-bf0d77d7d448e964e9716d5af67c48f3d014f090.zip | |
Scaffold basic project tree, implement benchmarking logic
Implement unit testing guardian
Diffstat (limited to 'benchmark')
| -rw-r--r-- | benchmark/benchmark.c | 101 | ||||
| -rw-r--r-- | benchmark/benchmark.h | 76 | ||||
| -rw-r--r-- | benchmark/resource_usage.c | 17 | ||||
| -rw-r--r-- | benchmark/resource_usage.h | 6 |
4 files changed, 200 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 | } | ||
diff --git a/benchmark/benchmark.h b/benchmark/benchmark.h new file mode 100644 index 0000000..a47e2a6 --- /dev/null +++ b/benchmark/benchmark.h | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #pragma once | ||
| 2 | /** | ||
| 3 | * @file benchmark.h | ||
| 4 | * @brief High-resolution benchmark helper for embedded IoT/student functions. | ||
| 5 | * | ||
| 6 | * Provides functions to measure average execution time, report results | ||
| 7 | * with colors, and mark slow tests. | ||
| 8 | * | ||
| 9 | * PASS CRITERIA: | ||
| 10 | * - The function returns expected result | ||
| 11 | * - Average execution time <= MAX_ALLOWED_MS | ||
| 12 | * | ||
| 13 | * WCET STRATEGY: | ||
| 14 | * - Average latency is measured; for worst-case, run representative | ||
| 15 | * worst-case inputs, measure maximum, and apply safety margin. | ||
| 16 | * | ||
| 17 | * CROSS-PLATFORM: | ||
| 18 | * - Linux / POSIX: uses clock_gettime(CLOCK_MONOTONIC) | ||
| 19 | * - Windows: uses QueryPerformanceCounter | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <stddef.h> | ||
| 23 | |||
| 24 | /** ANSI color codes for terminal output */ | ||
| 25 | extern const char *COLOR_RED; | ||
| 26 | extern const char *COLOR_GREEN; | ||
| 27 | extern const char *COLOR_YELLOW; | ||
| 28 | extern const char *COLOR_RESET; | ||
| 29 | |||
| 30 | /** Default maximum allowed average execution time in milliseconds */ | ||
| 31 | extern const double MAX_ALLOWED_MS; | ||
| 32 | |||
| 33 | /** Default benchmark iteration counts */ | ||
| 34 | extern const size_t DEFAULT_BENCHMARKS; | ||
| 35 | extern const size_t ITERATIONS_FAST; | ||
| 36 | extern const size_t ITERATIONS_SLOW; | ||
| 37 | |||
| 38 | /** Function pointer type for benchmarked functions */ | ||
| 39 | typedef int (*function_to_benchmark)(const char *arg); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * @brief Measure average execution time of a function. | ||
| 43 | * | ||
| 44 | * Runs the function `benchmarks` times, with warm-up, and returns | ||
| 45 | * average latency in milliseconds. | ||
| 46 | * | ||
| 47 | * @param func Function to benchmark. | ||
| 48 | * @param arg Argument to pass to function. | ||
| 49 | * @param benchmarks Number of iterations; 0 = DEFAULT_BENCHMARKS. | ||
| 50 | * @return Average latency per call in milliseconds. | ||
| 51 | */ | ||
| 52 | double benchmark_func(function_to_benchmark func, const char *arg, | ||
| 53 | size_t benchmarks); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * @brief Print benchmark result with colors and performance threshold. | ||
| 57 | * | ||
| 58 | * If passed = 0 OR avg_ms > max_allowed_ms, result is marked FAIL/SLOW. | ||
| 59 | * | ||
| 60 | * @param test_name Name of test. | ||
| 61 | * @param passed Function correctness result (1 = ok, 0 = fail) | ||
| 62 | * @param avg_ms Measured average latency | ||
| 63 | * @param max_allowed_ms Threshold for marking slow | ||
| 64 | */ | ||
| 65 | void report_result(const char *test_name, int passed, double avg_ms, | ||
| 66 | double max_allowed_ms); | ||
| 67 | |||
| 68 | /** | ||
| 69 | * @brief | ||
| 70 | * @param func | ||
| 71 | * @param arg | ||
| 72 | * @param benchmarks | ||
| 73 | * @param label | ||
| 74 | */ | ||
| 75 | void benchmark_func_with_resources(function_to_benchmark func, const char *arg, | ||
| 76 | size_t benchmarks, const char *label); | ||
diff --git a/benchmark/resource_usage.c b/benchmark/resource_usage.c new file mode 100644 index 0000000..04f7195 --- /dev/null +++ b/benchmark/resource_usage.c | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #include "resource_usage.h" | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <sys/resource.h> | ||
| 4 | #include <sys/time.h> | ||
| 5 | |||
| 6 | void print_resource_usage(const char *label) { | ||
| 7 | struct rusage usage; | ||
| 8 | if (getrusage(RUSAGE_SELF, &usage) == 0) { | ||
| 9 | long mem_kb = usage.ru_maxrss; | ||
| 10 | double user_sec = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1e6; | ||
| 11 | double sys_sec = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1e6; | ||
| 12 | printf("[%s] CPU user: %.3f s, system: %.3f s, peak memory: %ld KB\n", | ||
| 13 | label, user_sec, sys_sec, mem_kb); | ||
| 14 | } else { | ||
| 15 | printf("[%s] Resource usage not available\n", label); | ||
| 16 | } | ||
| 17 | } | ||
diff --git a/benchmark/resource_usage.h b/benchmark/resource_usage.h new file mode 100644 index 0000000..7e2015c --- /dev/null +++ b/benchmark/resource_usage.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #pragma once | ||
| 2 | /** | ||
| 3 | * | ||
| 4 | * @param label | ||
| 5 | */ | ||
| 6 | void print_resource_usage(const char *label); | ||
