feat: add benchmarking stdlib with memory safety checks and example scripts
Implement a new `benchmark` standard library module providing high-precision nanosecond timing functions (`bench_start`, `bench_end`, `bench_format`, `bench_format_auto`, `bench_reset`). Add a dedicated `memory.c` module with runtime memory safety validation, including stack overflow detection, local variable limit enforcement, and high-water mark tracking. Integrate memory checks into the interpreter's variable declaration path in `src/interpreter.c` and initialize the memory subsystem in `src/main.c`. Update the build system (`Makefile`) to compile the new source files and include benchmark tests in the test suite. Extend `TUTORIAL.md` with comprehensive documentation for the benchmarking API and random number functions (`rand`, `srand`, `rand_range`, `time`). Add two example scripts (`examples/bench.rc` and `examples/benchmark_demo.rc`) demonstrating performance measurement of recursive Fibonacci, loop iterations, list operations, string concatenation, object creation, and sorting algorithms.
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#include "unittest.rc"
|
||||
|
||||
int main() {
|
||||
int tc = new TestCase();
|
||||
tc.init("Benchmark Standard Library");
|
||||
|
||||
printf("\n=== Testing bench_start/bench_end ===\n");
|
||||
|
||||
int timer1 = bench_start();
|
||||
tc.assertGreaterEqual(timer1, 0, "bench_start returns valid timer ID");
|
||||
|
||||
int i = 0;
|
||||
int sum = 0;
|
||||
while (i < 1000) {
|
||||
sum = sum + i;
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
int elapsed1 = bench_end(timer1);
|
||||
tc.assertGreater(elapsed1, 0, "bench_end returns positive elapsed time");
|
||||
tc.assertLess(elapsed1, 1000000000, "elapsed time is less than 1 second");
|
||||
|
||||
printf("\n=== Testing multiple timers ===\n");
|
||||
|
||||
int timer2 = bench_start();
|
||||
int timer3 = bench_start();
|
||||
tc.assertNotEqual(timer2, timer3, "Multiple timers have different IDs");
|
||||
|
||||
int elapsed2 = bench_end(timer2);
|
||||
int elapsed3 = bench_end(timer3);
|
||||
tc.assertGreater(elapsed2, 0, "Timer 2 has positive elapsed time");
|
||||
tc.assertGreater(elapsed3, 0, "Timer 3 has positive elapsed time");
|
||||
|
||||
printf("\n=== Testing bench_format with nanoseconds ===\n");
|
||||
|
||||
char* formatted_ns = bench_format(12345, 0);
|
||||
tc.assertNotNull(formatted_ns, "bench_format(ns) returns non-null");
|
||||
tc.assertStringContains(formatted_ns, "ns", "Formatted string contains 'ns'");
|
||||
tc.assertStringContains(formatted_ns, "12345", "Formatted string contains value");
|
||||
|
||||
printf("\n=== Testing bench_format with microseconds ===\n");
|
||||
|
||||
char* formatted_us = bench_format(123456, 1);
|
||||
tc.assertNotNull(formatted_us, "bench_format(us) returns non-null");
|
||||
tc.assertStringContains(formatted_us, "us", "Formatted string contains 'us'");
|
||||
|
||||
printf("\n=== Testing bench_format with milliseconds ===\n");
|
||||
|
||||
char* formatted_ms = bench_format(1234567, 2);
|
||||
tc.assertNotNull(formatted_ms, "bench_format(ms) returns non-null");
|
||||
tc.assertStringContains(formatted_ms, "ms", "Formatted string contains 'ms'");
|
||||
|
||||
printf("\n=== Testing bench_format with seconds ===\n");
|
||||
|
||||
char* formatted_s = bench_format(1234567890, 3);
|
||||
tc.assertNotNull(formatted_s, "bench_format(s) returns non-null");
|
||||
tc.assertStringContains(formatted_s, "s", "Formatted string contains 's'");
|
||||
|
||||
printf("\n=== Testing bench_format with minutes ===\n");
|
||||
|
||||
char* formatted_min = bench_format(123456789000, 4);
|
||||
tc.assertNotNull(formatted_min, "bench_format(min) returns non-null");
|
||||
tc.assertStringContains(formatted_min, "min", "Formatted string contains 'min'");
|
||||
|
||||
printf("\n=== Testing bench_format with hours ===\n");
|
||||
|
||||
char* formatted_h = bench_format(7412345678900, 5);
|
||||
tc.assertNotNull(formatted_h, "bench_format(h) returns non-null");
|
||||
tc.assertStringContains(formatted_h, "h", "Formatted string contains 'h'");
|
||||
|
||||
printf("\n=== Testing bench_format_auto with nanoseconds ===\n");
|
||||
|
||||
char* auto_ns = bench_format_auto(500);
|
||||
tc.assertNotNull(auto_ns, "bench_format_auto(500ns) returns non-null");
|
||||
tc.assertStringContains(auto_ns, "ns", "Auto-format chooses nanoseconds for small values");
|
||||
|
||||
printf("\n=== Testing bench_format_auto with microseconds ===\n");
|
||||
|
||||
char* auto_us = bench_format_auto(5000);
|
||||
tc.assertNotNull(auto_us, "bench_format_auto(5000ns) returns non-null");
|
||||
tc.assertStringContains(auto_us, "us", "Auto-format chooses microseconds");
|
||||
|
||||
printf("\n=== Testing bench_format_auto with milliseconds ===\n");
|
||||
|
||||
char* auto_ms = bench_format_auto(5000000);
|
||||
tc.assertNotNull(auto_ms, "bench_format_auto(5ms) returns non-null");
|
||||
tc.assertStringContains(auto_ms, "ms", "Auto-format chooses milliseconds");
|
||||
|
||||
printf("\n=== Testing bench_format_auto with seconds ===\n");
|
||||
|
||||
char* auto_s = bench_format_auto(5000000000);
|
||||
tc.assertNotNull(auto_s, "bench_format_auto(5s) returns non-null");
|
||||
tc.assertStringContains(auto_s, "s", "Auto-format chooses seconds");
|
||||
|
||||
printf("\n=== Testing bench_format_auto with minutes ===\n");
|
||||
|
||||
char* auto_min = bench_format_auto(300000000000);
|
||||
tc.assertNotNull(auto_min, "bench_format_auto(5min) returns non-null");
|
||||
tc.assertStringContains(auto_min, "min", "Auto-format chooses minutes");
|
||||
|
||||
printf("\n=== Testing bench_format_auto with hours ===\n");
|
||||
|
||||
char* auto_h = bench_format_auto(18000000000000);
|
||||
tc.assertNotNull(auto_h, "bench_format_auto(5h) returns non-null");
|
||||
tc.assertStringContains(auto_h, "h", "Auto-format chooses hours");
|
||||
|
||||
printf("\n=== Testing practical benchmarking scenario ===\n");
|
||||
|
||||
int timer_practical = bench_start();
|
||||
|
||||
int j = 0;
|
||||
int factorial = 1;
|
||||
while (j < 100) {
|
||||
factorial = factorial + j;
|
||||
j = j + 1;
|
||||
}
|
||||
|
||||
int elapsed_practical = bench_end(timer_practical);
|
||||
char* practical_formatted = bench_format_auto(elapsed_practical);
|
||||
|
||||
tc.assertGreater(elapsed_practical, 0, "Practical benchmark measured time");
|
||||
tc.assertNotNull(practical_formatted, "Practical benchmark formatted correctly");
|
||||
|
||||
printf(" Practical test took: %s\n", practical_formatted);
|
||||
|
||||
printf("\n=== Testing bench_reset ===\n");
|
||||
|
||||
int reset_result = bench_reset();
|
||||
tc.assertEqual(reset_result, 0, "bench_reset returns 0");
|
||||
|
||||
int timer_after_reset = bench_start();
|
||||
tc.assertGreaterEqual(timer_after_reset, 0, "Timer ID valid after reset");
|
||||
|
||||
printf("\n=== Testing error cases ===\n");
|
||||
|
||||
int invalid_end = bench_end(9999);
|
||||
tc.assertEqual(invalid_end, -1, "bench_end with invalid timer returns -1");
|
||||
|
||||
int negative_timer = bench_end(-1);
|
||||
tc.assertEqual(negative_timer, -1, "bench_end with negative timer returns -1");
|
||||
|
||||
printf("\n=== Testing nested timing ===\n");
|
||||
|
||||
int outer_timer = bench_start();
|
||||
int inner_timer = bench_start();
|
||||
|
||||
int k = 0;
|
||||
while (k < 50) {
|
||||
k = k + 1;
|
||||
}
|
||||
|
||||
int inner_elapsed = bench_end(inner_timer);
|
||||
int outer_elapsed = bench_end(outer_timer);
|
||||
|
||||
tc.assertGreater(inner_elapsed, 0, "Inner timer measured time");
|
||||
tc.assertGreater(outer_elapsed, 0, "Outer timer measured time");
|
||||
tc.assertGreaterEqual(outer_elapsed, inner_elapsed, "Outer timer >= inner timer");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
if (tc.failed == 0) {
|
||||
printf("All benchmark stdlib tests PASSED!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -61,6 +61,35 @@ int main() {
|
||||
result_int = double_to_int(d_quot);
|
||||
tc.assertEqual(result_int, 1, "Double division: 5.0 / 3.0 = 1.666... (truncated to 1)");
|
||||
|
||||
printf("\n=== Testing Random Functions ===\n");
|
||||
srand(42);
|
||||
int r1 = rand();
|
||||
int r2 = rand();
|
||||
tc.assertNotEqual(r1, r2, "Two consecutive rand() calls should give different values");
|
||||
tc.assertGreaterEqual(r1, 0, "rand() returns non-negative");
|
||||
tc.assertGreaterEqual(r2, 0, "rand() returns non-negative");
|
||||
|
||||
srand(42);
|
||||
int r3 = rand();
|
||||
tc.assertEqual(r1, r3, "Same seed produces same first value");
|
||||
|
||||
printf("\n=== Testing rand_range ===\n");
|
||||
srand(time());
|
||||
int i = 0;
|
||||
int range_ok = 1;
|
||||
while (i < 100) {
|
||||
int val = rand_range(10, 20);
|
||||
if (val < 10 || val > 20) {
|
||||
range_ok = 0;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
tc.assertTrue(range_ok, "rand_range(10, 20) returns values in [10, 20]");
|
||||
|
||||
printf("\n=== Testing time ===\n");
|
||||
int t1 = time();
|
||||
tc.assertGreater(t1, 0, "time() returns positive timestamp");
|
||||
|
||||
printf("\n=== Summary ===\n");
|
||||
printf("Total Passed: %d\n", tc.passed);
|
||||
printf("Total Failed: %d\n", tc.failed);
|
||||
|
||||
Reference in New Issue
Block a user