bench: arm the Phase 4 gate -- proven to pass clean AND fire on a quadratic
El SDK CI - dev / build-and-test (pull_request) Failing after 12m7s

Adds tests/native/test_lexer_scaling.el, the regression gate for el #132.

Both directions are proven on LIVE workloads, not synthetic series:
  healthy per-character scan  1821 3251 6007 10422 us -> O(n)   PASS
  rescan-from-zero (the #132 shape)  922 3667 13524 44792 -> O(n^2) FAIL

A gate only proven to pass is decoration. The quadratic specimen exists so
the gate is proven to FIRE.

Also fixes elb_spread_ok to judge the ASYMPTOTIC TAIL (last three ratios)
rather than the whole sweep. Measured on a genuinely linear scan the ratios
ran 3.37 2.92 1.76 1.65 -- the head looks quadratic because it is cold
cache, the tail is the truth. Whole-sweep spread rejected correct data. A
complexity bound is an asymptotic claim and must be judged asymptotically.

That fix came from the classifier refusing to rubber-stamp my own bad
measurement: it reported INDETERMINATE on an unwarmed sweep rather than
passing it. Warmup is now taken and discarded at every sweep point.

Reverts the == workarounds in test_elbench.el now that el #137 has landed;
the natural form generates no str_eq and all 13 fitter tests stay green.
The  workaround remains -- the Plus arm is still open.
This commit is contained in:
Neuron
2026-08-15 21:58:46 -05:00
parent cf060adbfd
commit e0b2c0ea54
3 changed files with 210 additions and 40 deletions
+17 -37
View File
@@ -20,78 +20,63 @@ fn _s4(a: Int, b: Int, c: Int, d: Int) -> [Int] {
test "classifies a linear allocation series as O(n)" {
// fitprobe `linear`, allocation count
let v = _s4(208, 409, 810, 1611)
let c: Int = elb_measured_curve(v, 10)
assert c == 2, "linear allocs should classify O(n)"
assert elb_measured_curve(v, 10) == 2, "linear allocs should classify O(n)"
}
test "classifies a linear byte series as O(n)" {
// fitprobe `linear`, allocation bytes
let v = _s4(4786, 9682, 19474, 39658)
let c: Int = elb_measured_curve(v, 10)
assert c == 2, "linear bytes should classify O(n)"
assert elb_measured_curve(v, 10) == 2, "linear bytes should classify O(n)"
}
test "classifies a quadratic byte series as O(n^2)" {
// fitprobe `accum`, allocation bytes -- the accumulator-rebuild shape
let v = _s4(20300, 80600, 321200, 1282400)
let c: Int = elb_measured_curve(v, 10)
assert c == 4, "accum bytes should classify O(n^2)"
assert elb_measured_curve(v, 10) == 4, "accum bytes should classify O(n^2)"
}
test "accumulator count is linear -- proves count alone misses it" {
// Same run as above. The COUNT is exactly linear while bytes are
// quadratic. A count-only gate passes this defect clean.
let v = _s4(200, 400, 800, 1600)
let c: Int = elb_measured_curve(v, 10)
assert c == 2, "accum count classifies O(n)"
let g: Int = elb_gate(v, 2, 10)
assert g == 0, "count-only gate PASSES the quadratic"
assert elb_measured_curve(v, 10) == 2, "accum count classifies O(n)"
assert elb_gate(v, 2, 10) == 0, "count-only gate PASSES the quadratic"
}
test "classifies a quadratic time series as O(n^2)" {
// fitprobe `compute` -- el #132's shape: n scans over n characters
let v = _s4(67, 205, 818, 3268)
let c: Int = elb_measured_curve(v, 10)
assert c == 4, "compute time should classify O(n^2)"
assert elb_measured_curve(v, 10) == 4, "compute time should classify O(n^2)"
}
test "REFUSES an all-zero series instead of calling it O(1)" {
// fitprobe `compute` allocation count. Pure CPU, allocates nothing.
// Reporting O(1) here would be a confident answer with nothing behind it.
let v = _s4(0, 0, 0, 0)
let g: Int = elb_gate(v, 2, 10)
assert g == 3, "all-zero series must be REFUSED"
// NOTE: bind before comparing. `call(...) == <non-literal>` lowers to str_eq()
// on integers and segfaults -- see the elc == inference bug reported with
// this change. `let x = call(); x == y` is the safe form.
let got: Int = elb_measured_curve(v, 10)
assert got < 0, "unclassifiable returns -1"
assert elb_gate(v, 2, 10) == 3, "all-zero series must be REFUSED"
assert elb_measured_curve(v, 10) < 0, "unclassifiable returns -1"
}
test "REFUSES an implausibly flat series" {
// The shape produced when clang closes a loop to a multiply: a real
// answer, no work done, no movement across an 8x input range.
let v = _s4(1000, 1001, 1002, 1003)
let g: Int = elb_gate(v, 2, 10)
assert g == 3, "hard-flat series must be REFUSED"
assert elb_gate(v, 2, 10) == 3, "hard-flat series must be REFUSED"
}
test "gate FAILS a quadratic declared as linear" {
let v = _s4(20300, 80600, 321200, 1282400)
let g: Int = elb_gate(v, 2, 10)
assert g == 1, "O(n^2) measured vs O(n) declared must FAIL"
assert elb_gate(v, 2, 10) == 1, "O(n^2) measured vs O(n) declared must FAIL"
}
test "gate PASSES a linear series declared as linear" {
let v = _s4(208, 409, 810, 1611)
let g: Int = elb_gate(v, 2, 10)
assert g == 0, "O(n) measured vs O(n) declared must PASS"
assert elb_gate(v, 2, 10) == 0, "O(n) measured vs O(n) declared must PASS"
}
test "gate reports BETTER when measured beats the declared bound" {
let v = _s4(208, 409, 810, 1611)
let g: Int = elb_gate(v, 4, 10)
assert g == 4, "O(n) measured vs O(n^2) declared is BETTER"
assert elb_gate(v, 4, 10) == 4, "O(n) measured vs O(n^2) declared is BETTER"
}
test "gate reports INDETERMINATE on disagreeing ratios" {
@@ -100,13 +85,11 @@ test "gate reports INDETERMINATE on disagreeing ratios" {
// honest answer is "cannot tell", not a classification -- this is exactly
// why benchmarks need auto-scaled iteration counts rather than one shot.
let v = _s4(26, 19, 43, 78)
let g: Int = elb_gate(v, 2, 10)
assert g == 2, "disagreeing ratios must be INDETERMINATE"
assert elb_gate(v, 2, 10) == 2, "disagreeing ratios must be INDETERMINATE"
}
test "black_box is a real barrier and returns its input" {
let bb: Int = el_black_box(42)
assert bb == 42, "black_box is value-preserving"
assert el_black_box(42) == 42, "black_box is value-preserving"
let s: Int = 0
let i: Int = 0
while i < 100 {
@@ -121,11 +104,8 @@ test "black_box is a real barrier and returns its input" {
}
test "curve names round-trip" {
let k1: Int = elb_curve_from_name("O(n)")
assert k1 == 2, "O(n) parses"
let k2: Int = elb_curve_from_name("O(n^2)")
assert k2 == 4, "O(n^2) parses"
assert elb_curve_from_name("O(n)") == 2, "O(n) parses"
assert elb_curve_from_name("O(n^2)") == 4, "O(n^2) parses"
assert str_eq(elb_curve_name(4), "O(n^2)"), "O(n^2) renders"
let unk: Int = elb_curve_from_name("O(nonsense)")
assert unk < 0, "unknown curve is -1"
assert elb_curve_from_name("O(nonsense)") < 0, "unknown curve is -1"
}