From bf0d77d7d448e964e9716d5af67c48f3d014f090 Mon Sep 17 00:00:00 2001 From: Filip Wandzio Date: Sun, 1 Mar 2026 01:03:39 +0100 Subject: Scaffold basic project tree, implement benchmarking logic Implement unit testing guardian --- benchmark/benchmark.c | 101 +++++++++++++++++++++++++++++++++++++++++++++ benchmark/benchmark.h | 76 ++++++++++++++++++++++++++++++++++ benchmark/resource_usage.c | 17 ++++++++ benchmark/resource_usage.h | 6 +++ 4 files changed, 200 insertions(+) create mode 100644 benchmark/benchmark.c create mode 100644 benchmark/benchmark.h create mode 100644 benchmark/resource_usage.c create mode 100644 benchmark/resource_usage.h (limited to 'benchmark') 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 @@ +#define _POSIX_C_SOURCE 199309L +#include "benchmark.h" +#include "resource_usage.h" +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +/* ANSI color codes */ +const char* COLOR_RED = "\033[31m"; +const char* COLOR_GREEN = "\033[32m"; +const char* COLOR_YELLOW = "\033[33m"; +const char* COLOR_RESET = "\033[0m"; + +/* Maximum allowed average execution time per call in milliseconds */ +const double MAX_ALLOWED_MS = 0.05; + +/* Default benchmark iteration counts */ +const size_t DEFAULT_BENCHMARKS = 100; +const size_t ITERATIONS_FAST = 1000; +const size_t ITERATIONS_SLOW = 10; + +/* Volatile sink to prevent compiler optimizations removing the function call */ +static volatile int sink; + +/** + * @brief Cross-platform monotonic time in milliseconds + */ +static double now_ms(void) +{ +#if defined(_WIN32) || defined(_WIN64) + LARGE_INTEGER freq, counter; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + return (double)counter.QuadPart * 1000.0 / (double)freq.QuadPart; +#else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6; +#endif +} + +double benchmark_func(const function_to_benchmark func, + const char* arg, + size_t benchmarks) +{ + if (!func) + return 0.0; + if (benchmarks == 0) + benchmarks = DEFAULT_BENCHMARKS; + size_t warmup = benchmarks / 10; + if (warmup > 1000) + warmup = 1000; + for (size_t i = 0; i < warmup; ++i) + sink = func(arg); + const double start = now_ms(); + for (size_t i = 0; i < benchmarks; ++i) + sink = func(arg); + const double end = now_ms(); + return (end - start) / benchmarks; +} + +void report_result(const char* test_name, + const int passed, + const double avg_ms, + const double max_allowed_ms) +{ + const int is_pass = passed && (avg_ms <= max_allowed_ms); + const char* status_color = is_pass ? COLOR_GREEN : COLOR_RED; + const char* status_text = is_pass ? "PASS" : "FAIL"; + + if (!is_pass && avg_ms > max_allowed_ms) + status_text = "SLOW"; + + printf("[avg %.6f ms] %s%s: %s%s\n", + avg_ms, + status_color, + status_text, + test_name, + COLOR_RESET); + + if (!is_pass && avg_ms > max_allowed_ms) { + printf(" %sReason:%s exceeded %.3f ms limit\n", + COLOR_YELLOW, + COLOR_RESET, + max_allowed_ms); + } +} + +void benchmark_func_with_resources(const function_to_benchmark func, + const char* arg, + const size_t benchmarks, + const char* label) +{ + const double avg_ms = benchmark_func(func, arg, benchmarks); + printf("[Benchmark] %s avg %.6f ms\n", label, avg_ms); + print_resource_usage(label); +} 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 @@ +#pragma once +/** + * @file benchmark.h + * @brief High-resolution benchmark helper for embedded IoT/student functions. + * + * Provides functions to measure average execution time, report results + * with colors, and mark slow tests. + * + * PASS CRITERIA: + * - The function returns expected result + * - Average execution time <= MAX_ALLOWED_MS + * + * WCET STRATEGY: + * - Average latency is measured; for worst-case, run representative + * worst-case inputs, measure maximum, and apply safety margin. + * + * CROSS-PLATFORM: + * - Linux / POSIX: uses clock_gettime(CLOCK_MONOTONIC) + * - Windows: uses QueryPerformanceCounter + */ + +#include + +/** ANSI color codes for terminal output */ +extern const char *COLOR_RED; +extern const char *COLOR_GREEN; +extern const char *COLOR_YELLOW; +extern const char *COLOR_RESET; + +/** Default maximum allowed average execution time in milliseconds */ +extern const double MAX_ALLOWED_MS; + +/** Default benchmark iteration counts */ +extern const size_t DEFAULT_BENCHMARKS; +extern const size_t ITERATIONS_FAST; +extern const size_t ITERATIONS_SLOW; + +/** Function pointer type for benchmarked functions */ +typedef int (*function_to_benchmark)(const char *arg); + +/** + * @brief Measure average execution time of a function. + * + * Runs the function `benchmarks` times, with warm-up, and returns + * average latency in milliseconds. + * + * @param func Function to benchmark. + * @param arg Argument to pass to function. + * @param benchmarks Number of iterations; 0 = DEFAULT_BENCHMARKS. + * @return Average latency per call in milliseconds. + */ +double benchmark_func(function_to_benchmark func, const char *arg, + size_t benchmarks); + +/** + * @brief Print benchmark result with colors and performance threshold. + * + * If passed = 0 OR avg_ms > max_allowed_ms, result is marked FAIL/SLOW. + * + * @param test_name Name of test. + * @param passed Function correctness result (1 = ok, 0 = fail) + * @param avg_ms Measured average latency + * @param max_allowed_ms Threshold for marking slow + */ +void report_result(const char *test_name, int passed, double avg_ms, + double max_allowed_ms); + +/** + * @brief + * @param func + * @param arg + * @param benchmarks + * @param label + */ +void benchmark_func_with_resources(function_to_benchmark func, const char *arg, + 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 @@ +#include "resource_usage.h" +#include +#include +#include + +void print_resource_usage(const char *label) { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + long mem_kb = usage.ru_maxrss; + double user_sec = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1e6; + double sys_sec = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1e6; + printf("[%s] CPU user: %.3f s, system: %.3f s, peak memory: %ld KB\n", + label, user_sec, sys_sec, mem_kb); + } else { + printf("[%s] Resource usage not available\n", label); + } +} 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 @@ +#pragma once +/** + * + * @param label + */ +void print_resource_usage(const char *label); -- cgit v1.2.3