summaryrefslogtreecommitdiffstats
path: root/benchmark
diff options
context:
space:
mode:
authorFilip Wandzio <contact@philw.dev>2026-03-01 01:03:39 +0100
committerFilip Wandzio <contact@philw.dev>2026-03-01 01:03:39 +0100
commitbf0d77d7d448e964e9716d5af67c48f3d014f090 (patch)
treee55f1e91a8c20cd737dfb01dc12a954c25711e01 /benchmark
downloadembedded_guardian-bf0d77d7d448e964e9716d5af67c48f3d014f090.tar.gz
embedded_guardian-bf0d77d7d448e964e9716d5af67c48f3d014f090.zip
Scaffold basic project tree, implement benchmarking logic
Implement unit testing guardian
Diffstat (limited to '')
-rw-r--r--benchmark/benchmark.c101
-rw-r--r--benchmark/benchmark.h76
-rw-r--r--benchmark/resource_usage.c17
-rw-r--r--benchmark/resource_usage.h6
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 */
13const char* COLOR_RED = "\033[31m";
14const char* COLOR_GREEN = "\033[32m";
15const char* COLOR_YELLOW = "\033[33m";
16const char* COLOR_RESET = "\033[0m";
17
18/* Maximum allowed average execution time per call in milliseconds */
19const double MAX_ALLOWED_MS = 0.05;
20
21/* Default benchmark iteration counts */
22const size_t DEFAULT_BENCHMARKS = 100;
23const size_t ITERATIONS_FAST = 1000;
24const size_t ITERATIONS_SLOW = 10;
25
26/* Volatile sink to prevent compiler optimizations removing the function call */
27static volatile int sink;
28
29/**
30 * @brief Cross-platform monotonic time in milliseconds
31 */
32static 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
46double 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
66void 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
93void 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 */
25extern const char *COLOR_RED;
26extern const char *COLOR_GREEN;
27extern const char *COLOR_YELLOW;
28extern const char *COLOR_RESET;
29
30/** Default maximum allowed average execution time in milliseconds */
31extern const double MAX_ALLOWED_MS;
32
33/** Default benchmark iteration counts */
34extern const size_t DEFAULT_BENCHMARKS;
35extern const size_t ITERATIONS_FAST;
36extern const size_t ITERATIONS_SLOW;
37
38/** Function pointer type for benchmarked functions */
39typedef 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 */
52double 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 */
65void 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 */
75void 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
6void 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 */
6void print_resource_usage(const char *label);