Zulip Chat Archive
Stream: lean4
Topic: Exponential blowup in compilation time
Leni Aniva (Jul 04 2025 at 07:05):
I have a series of function calls like this:
let (.some boo, a) <- function boo a | fail "incorrect"
checkEq boo ..
let (.some boo, a) <- function boo a | fail "incorrect"
checkEq boo ..
let (.some boo, a) <- function boo a | fail "incorrect"
checkEq boo ..
let (.some boo, a) <- function boo a | fail "incorrect"
checkEq boo ..
and with every pair of lines, the compilation time seems to exponentially increase. e.g. with just two pairs, it generated 78M of prof data, but with three pairs it generated 8G of data, and 4 pairs generated 80G of data.
I profiled the lean executable running on this particular file using perf, and these functions have the highest cycle count:
image.png
With the highest one being lean::replace_rec_fn::apply(lean::expr const&, unsigned int).
Is there a way pinpoint the root cause of the blowup?
Cameron Zwarich (Jul 04 2025 at 15:01):
Which version of Lean are you using, and what is the full stack trace of those replace_rec_fn calls?
Leni Aniva (Jul 04 2025 at 16:59):
Cameron Zwarich said:
Which version of Lean are you using, and what is the full stack trace of those
replace_rec_fncalls?
4.21
The flamegraph looks like this
Sebastian Ullrich (Jul 04 2025 at 18:04):
Does trace.profiler give a better answer? It doesn't have any stack size limit at the very least
Leni Aniva (Jul 04 2025 at 18:17):
I found a fix, which involves changing
checkTrue "boo" $ boo matches .branch _ `Name
to
checkTrue "boo" $ boo matches .branch ..
and the compilation sped up by a couple hundred times
Leni Aniva (Jul 04 2025 at 18:20):
Sebastian Ullrich said:
Does trace.profiler give a better answer? It doesn't have any stack size limit at the very least
No it just outputs a bunch of elaborator messages
Leni Aniva (Jul 04 2025 at 19:35):
In the same compilation unit, adding more code causes it to compile faster.
This takes 5.71s
checkTrue "[2] boo" $ boo matches .branch _ `Name
This takes 520ms
let .branch _ n := boo | fail "Incorrect"
checkEq "[2] boo" n `Name
checkTrue "[2] boo" $ boo matches .branch _ `Name
Justin Asher (Jul 04 2025 at 20:19):
Do you know why this is so much faster?
Leni Aniva (Jul 04 2025 at 20:28):
Justin Asher said:
Do you know why this is so much faster?
no but it seems to be related to the exponential compile time problem
Cameron Zwarich (Jul 04 2025 at 22:12):
Leni Aniva said:
Cameron Zwarich said:
Which version of Lean are you using, and what is the full stack trace of those
replace_rec_fncalls?4.21
Is it possible for you to try this code with 4.22 rc3? I'm curious if it's fixed by the new compiler. If it's not, would it be possible for you to share this example with me?
Last updated: Dec 20 2025 at 21:32 UTC