Test "perf/shift-cascade"
Expected: 👍 accept · Size: 256.3 KB · Lines: 5.1 k · lean4export: 3.1.0 · Lean: 4.29.0 · 📄 Declaration · 🔗 Source
Stress test for cascading substitution overhead in kernel let processing.
N nested let bindings inside a lambda, where each value references the
outer lambda parameter and the previous binding:
fun (a : Nat → Nat) => let f₁ := fun x => a x let f₂ := fun x => a (f₁ x) ... let fₙ := fun x => a (fₙ₋₁ x) fₙ 0
The kernel processes each let by substituting the value into the body.
Each value has a free bvar (references a), so substitution under inner
binders creates shifted copies. In a de Bruijn kernel with deferred shifts,
these Shift(val, offset) wrappers accumulate: step k must traverse
through O(k) wrappers from previous steps, giving O(N²) total work.
A locally-nameless kernel substitutes fvars that need no shifting, giving O(N) total.
N=1000 in the Lean source. Increase to stress further.
| Checker | Result | ⏱️ | 🧠 | ||
|---|---|---|---|---|---|
| nanoda | 👍 | 8 ms | (-84%) | 4.1 MB | |
| official-nightly | 👍 | 52 ms | (-3%) | 58.4 MB | |
| official | 👍 | 54 ms | (0%) | 66.7 MB | |
| official-v4.28.0 | 👍 | 60 ms | (+11%) | 71.8 MB | |
| nanobruijn | 👍 | 204 ms | (+279%) | 66.2 MB | |
| lean4lean | 👍 | 84 ms | (+55%) | 92.9 MB | |
| mini | 👍 | 1.8 s | (+3285%) | 70.5 MB | |
| always-decline | 🚫 | 1 ms | 2.8 MB | ||
| always-reject | ✋ | 1 ms | 2.8 MB | ||
| rpylean | 👍 | 14 ms | (-75%) | 14.8 MB | |
| parse-only | 👍 | 55 ms | (+2%) | 70.0 MB | |
| always-accept | 👍 | 1 ms | (-99%) | 2.8 MB |