// fitprobe.el — controlled growth-curve specimens for validating the complexity fitter. // // Three deliberately-shaped workloads. None depends on a real defect existing, // which is the point: the fitter must be provable against KNOWN curves. // // linear — one allocation per item. count O(n), bytes O(n), time O(n) // accum — rebuilds its accumulator. count O(n), bytes O(n^2), time O(n^2) // compute — nested arithmetic, no alloc. count O(1), bytes O(1), time O(n^2) // // `compute` is the specimen that matters. It is the shape of el #132 // (strlen-per-character inside str_char_code): pure CPU, zero allocation. // An allocation-only gate is structurally blind to it. // // No imports — uses runtime builtins directly so nothing collides. fn work_linear(n: Int) -> Int { let parts: [String] = native_list_empty() let i: Int = 0 while i < n { let parts = native_list_append(parts, int_to_str(i)) let i = i + 1 } return native_list_len(parts) } fn work_accum(n: Int) -> Int { let acc: String = "" let i: Int = 0 while i < n { let acc = acc + "x" let i = i + 1 } return str_len(acc) } fn work_compute(n: Int) -> Int { // str_char_code is an opaque external call, so the C optimiser cannot // reduce this nest to a closed form the way it does with `total + 1`. // This is the exact shape of el #132: n scans over n characters, pure // CPU, ZERO allocation. let s: String = "abcdefghij" let total: Int = 0 let i: Int = 0 while i < n { let j: Int = 0 while j < n { let total = total + str_char_code(s, 0) let j = j + 1 } let i = i + 1 } return total } fn run_one(mode: String, n: Int) { let c0: Int = el_alloc_count() let b0: Int = el_alloc_bytes() let t0: Int = el_now_instant() let r: Int = 0 if str_eq(mode, "linear") { let r = work_linear(n) } if str_eq(mode, "accum") { let r = work_accum(n) } if str_eq(mode, "compute") { let r = work_compute(n) } let t1: Int = el_now_instant() let c1: Int = el_alloc_count() let b1: Int = el_alloc_bytes() println(mode + "\t" + int_to_str(n) + "\t" + int_to_str(c1 - c0) + "\t" + int_to_str(b1 - b0) + "\t" + int_to_str((t1 - t0) / 1000) + "\t" + int_to_str(r)) return } fn sweep(mode: String) { run_one(mode, 200) run_one(mode, 400) run_one(mode, 800) run_one(mode, 1600) return } fn main() -> Int { println("mode\tn\tallocs\tbytes\tusec\tsink") sweep("linear") sweep("accum") sweep("compute") return 0 }