lean4-htt/tests/elab_bench/string_simp_ne.lean
Joachim Breitner 26ad4d6972
feat: name the functional argument to brecOn in structural recursion (#12987)
This PR extracts the functional (lambda) passed to `brecOn` in
structural
recursion into a named `_f` helper definition (e.g. `foo._f`), similar
to
how well-founded recursion uses `._unary`. This way the functional shows
up
with a helpful name in kernel diagnostics rather than as an anonymous
lambda.

The `_f` definition is added with `.abbrev` kernel reducibility hints
and
the `@[reducible]` elaborator attribute, so the kernel unfolds it
eagerly
after `brecOn` iota-reduces. For inductive predicates, the previous
inline
lambda behavior is kept.

To ensure that parent definitions still get the correct reducibility
height
(since `getMaxHeight` ignores `.abbrev` definitions), each `_f`'s body
height is registered via a new `defHeightOverrideExt` environment
extension.
`getMaxHeight` checks this extension for all definitions, making the
height
computation transparent to the extraction.

This change improves code size (a bit). It may regress kernel reduction
times,
especially if a function defined by structural recursion is used in
kernel reduction
proofs on the hot path. Functions defined by structural recursion are
not particularly
fast to reduce anyways (due to the `.brecOn` construction), so already
now it may be
worth writing a kernel-reduction-friendly function manually (using the
recursor directly,
avoiding overloaded operations). This change will guide you in knowing
which function to
optimize.


🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:40:18 +00:00

62 lines
2.4 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Lean
/-!
Benchmark: String equality/inequality simproc kernel efficiency.
Tests `simp` on string literal (in)equality for various string lengths.
The kernel verifies proof terms from `String.reduceEq`/`String.reduceNe`.
Key variables:
- String length: affects `String.ofList` kernel evaluation (unavoidable O(n))
- Position of first difference: with the `congrArg List.get?Internal` optimization,
the inequality proof cost is O(first_diff_pos) instead of O(string_length)
-/
set_option Elab.async false
set_option maxHeartbeats 8000000
open Lean Elab Command in
/-- Generate `example : s₁ ≠ s₂ := by simp` where s₁ = n×'a'++"x" and s₂ = n×'a'++"y". -/
elab "#bench_string_ne_suffix " n:num : command => do
let n := n.getNat
let pfx := String.ofList (List.replicate n 'a')
let s1 := pfx ++ "x"
let s2 := pfx ++ "y"
elabCommand (← `(#time example : ($(Syntax.mkStrLit s1) : String) ≠ ($(Syntax.mkStrLit s2) : String) := by simp))
open Lean Elab Command in
/-- Generate `example : s₁ ≠ s₂ := by simp` where s₁ = "x"++n×'a' and s₂ = "y"++n×'a'.
Strings differ at the first character — tests O(1) inequality proof. -/
elab "#bench_string_ne_prefix " n:num : command => do
let n := n.getNat
let sfx := String.ofList (List.replicate n 'a')
let s1 := "x" ++ sfx
let s2 := "y" ++ sfx
elabCommand (← `(#time example : ($(Syntax.mkStrLit s1) : String) ≠ ($(Syntax.mkStrLit s2) : String) := by simp))
open Lean Elab Command in
/-- Generate `example : s = s := by simp` with s = n×'a'. -/
elab "#bench_string_eq " n:num : command => do
let n := n.getNat
let s := String.ofList (List.replicate n 'a')
elabCommand (← `(#time example : ($(Syntax.mkStrLit s) : String) = ($(Syntax.mkStrLit s) : String) := by simp))
-- Ne: shared prefix of increasing length (differ at last character)
-- Tests scaling of the full proof pipeline
#bench_string_ne_suffix 0
#bench_string_ne_suffix 10
#bench_string_ne_suffix 100
#bench_string_ne_suffix 500
#bench_string_ne_suffix 1000
#bench_string_ne_suffix 2000
-- Ne: long strings that differ at the first character
-- The congrArg optimization makes the inequality proof O(1);
-- remaining cost is String.ofList kernel verification
#bench_string_ne_prefix 1000
#bench_string_ne_prefix 2000
-- Eq: identical strings of increasing length (constant time with eq_true rfl)
#bench_string_eq 10
#bench_string_eq 500
#bench_string_eq 2000