Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ fix ] Exponentially reduce memory consumption for elab scripts running #3194

Merged
merged 1 commit into from
Jan 28, 2024

Conversation

buzden
Copy link
Contributor

@buzden buzden commented Jan 17, 2024

It's been noticed for a long time that memory consumption of the compiler executing elaborator scripts somewhat exponential on the reached depth of monadic bindings in the Elab monad. At the same time, it's been noticed that code like act1 *> act2 <* act3 uses much more memory comparing to the code do {act1; r <- act2; act3; pure r} when it is somewhere deep inside the script. My guess is that this is because of a closures preserving problem, similar to runtime problems of closure allocation.

Since both map and <*> were implemented through Bind in the Elab monad, I thought that running them in Core is much more efficient (because Core is effectively a glamourous IO, which is run optimised, with no cost for bindings). I tried this, but I didn't suspect how efficient this would be! My guess is that reduction of binds-depth severely reduces unneeded job of closures preserving in the evaluator.

Currently, one of my big elaborator scripts, which runs on the current main 3 hours 17 minutes taking 16.5 Gb of memory, runs just 2 hours 24 minutes using 10 Gb with this patch (both runs were on the same machine using the patched GC from Edwin). More impressively, another elaborator script that used more than 360 Gb of memory unabling to finish due to out of memory error, successfully finishes using just 10 Gb of memory with the change from this PR.

Those test are large and use a lot of dependencies, and it's really hard to make a small test out of them (and anyway, those cases run for several hours). I didn't manage to make a small test that exploits the problem being fixed.

In the implementation in managing newly added Map and Ap I used applyToStack function that is used in the management of the Bind constructor to apply arbitrary NF to an abribtrary closure. Unlike the Bind case, I used the weakest EvalOpts in order to not to reduce expressions too much (in fact, to preserve the status quo behaviour of produced expressions). I extended one exitsting test to make sure behaviour preserves in a different case too.

@gallais
Copy link
Member

gallais commented Jan 17, 2024

Impressive work!

@andrevidela
Copy link
Collaborator

My guess is that this is because of a closures preserving problem, similar to runtime problems of closure allocation.

implicit captures are evil for performance, definitely something we need to keep an eye on.

@andrevidela andrevidela merged commit dd95026 into idris-lang:main Jan 28, 2024
22 checks passed
@buzden buzden deleted the elab-treat-spec-map-ap branch January 28, 2024 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants