bench
Benchmarking and performance measurement - timers, benchmark runners with warmup, benchmark suites, comparisons, microbenchmarks sorted by speed, profiling by name, memory tracking, and JSON/HTML export.
Load with: use bench
What this module does
bench measures how fast your ilusm code runs. You wrap a function
in a benchmark, pick an iteration count, and call brun - it does a warmup
pass (10% of iterations by default), times the real run, and returns total time,
time per operation, and operations per second.
You can group multiple benchmarks into a suite, compare ilusm against a reference implementation, run a set of microbenchmarks and rank them fastest-first, profile named code sections across multiple calls, and export results to JSON or HTML.
Quick example
use bench
# Benchmark a single function
b = bebne("my sort", \() trl.srt([5,3,1,4,2]), 10000)
result = brun(b)
bprin(result)
# my sort:
# 10000 iterations in 42.50ms
# 0.0043ms per op
# 234567 ops/sec
# Compare two implementations
bcomp("sort", \() trl.srt(data), \() nil, "reference", 5000)
# Run a suite
s = bsuit("String ops")
s = bsuit(s, bebne("concat", \() "a" + "b", 50000))
s = bsuit(s, bebne("repeat", \() txt.rep("x", "x", "y"), 50000))
bsuit(s)
Functions
Timer
btime is overloaded - which behaviour runs depends on the arguments.
btime()
Creates a new timer. Records start: tim.now() and an empty laps list.
btime(timer)
Resets the timer's start time to now. Returns the timer.
btime(timer, name)
Records a named lap - stores {name, time: elapsed_since_last_start} in timer.laps and resets the start. Returns the timer.
btime(timer)
Returns the elapsed milliseconds since the last start without recording a lap.
Single benchmark
bebne(name, fn, iterations)
Creates a benchmark descriptor. warmup is set to iterations / 10. The benchmark hasn't run yet - call brun to execute it.
brun(benchmark)
Runs the benchmark. First executes warmup iterations (discarded). Then times iterations calls to benchmark.fn(). Returns {name, iterations, total_ms, per_op_ms, ops_per_sec}.
bprin(result)
Prints a formatted result to the log: name, iteration count, total time, time per op, and ops/sec - all formatted to 2–4 decimal places.
Benchmark suite
bsuit is overloaded.
bsuit(name)
Creates a new suite with a name, empty benchmark list, and empty results list.
bsuit(suite, benchmark)
Adds a benchmark to the suite. Returns the updated suite.
bsuit(suite)
Runs all benchmarks in the suite in order, printing each result and a total time summary. Returns the list of results.
Comparison
bcomp(name, ilusm_fn, ref_fn, ref_name, iterations)
Runs an ilusm benchmark and a reference implementation side by side. ref_fn is a zero-argument function that returns a result object in the same shape as brun, or nil if unavailable. Prints the speedup ratio (e.g. "ilusm is 1.3x faster than reference").
Microbenchmarks
bmicr(name, operations, iterations)
Runs a list of {name, fn} operations as individual benchmarks. Sorts results by per_op_ms ascending (fastest first) and prints a ranked list. Returns the unsorted results list.
Profiling
bprof is overloaded.
bprof()
Creates a new profile object with an empty calls map and no current section.
bprof(profile, name)
Starts timing a named section. Sets profile.current to {name, start: tim.now()}.
bprof(profile)
Ends the current section. Accumulates elapsed time and call count under the section name in profile.calls. Clears profile.current.
bprof(profile)
Prints the profile report - all section names with call count, total ms, and average ms per call.
Memory tracking
bmems()
Returns current memory stats via the host native __bench_mem_stats(). The shape of the returned object depends on the host runtime.
bmemd(before, after)
Computes the delta between two memory snapshots. Returns {heap_delta, objects_delta}.
Export
bexpo is overloaded.
bexpo(results)
Exports results as a JSON string with a timestamp and a benchmarks array.
bexpo(results)
Exports results as an HTML table with columns: Name, Iterations, Total (ms), Per Op (ms), Ops/sec.
Notes
btime,bsuit,bprof, andbexpoare all overloaded - which overload runs depends on the number and types of arguments.- Warmup is always
floor(iterations / 10). For very low iteration counts this may be 0. - Memory tracking requires the host runtime to provide
__bench_mem_stats. - Requires
tim,txt, andjsn.