ilusm.dev

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, and bexpo are 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, and jsn.