-
Notifications
You must be signed in to change notification settings - Fork 446
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
Garbage collection happens too late resulting in huge memory consumption #5023
Comments
Update: the last mistery with P4Program being held is solved. It is in |
Tagging @vlstill @fruffy @ChrisDodd for insights. The case outlined above is pretty mild. I saw other cases of erratic behavior:
|
I suspect one problem would be a common pattern in visitors that collect information during the run and clear internal state in One alternative could be to allow a pass manager to (instead of passes) hold pass factory functions that preserve the construction parameters and construct passes only before they are run and destruct them after the run. This would also strongly discourage cross-talk between repeated runs of a pass. Another problem could be in local pass managers (which are called e.g. from frontend) which heap-allocate a state that is shared across the passes (or access info from earlier passes by later passes in the manager). Again, such state can easily become a new root released only when the (nested) pass manager is released. Here, clearing the memory requires even more work than just an I am not sure how wide-spread these patterns are in frontend. It might be worth investigating starting from passes which are known to retain a lot of memory. Sadly, I'd say that by applying GC to everything (as opposed to e.g. apply it only to IR nodes), the original P4C authors created an illusion that memory is not a problem. This discourages thinking about memory lifetimes, which eventually becomes a problem :-(. |
I spent quite some time on this, and there are lots of cases like this. Typical scenario is that some information is collected in several inspectors, then passed down to transform, etc. Certainly, the internal state is almost never cleared, many passes implicitly assumes that they are only run once. Those who have to deal with the state cleanup is ones running in
The cheap alternative is for existing PassManager to destroy passes after they are applied. This essentially implies that passes got executed once (certainly, PassRepeated would need to kill everything in the end), but maybe this could be solved via explicit |
Recently I was profiling the memory consumption of some large apps and it turns out that the memory allocation / deallocation pattern is very concerning.
I collected the GC used memory statistics as reported via
-T pass_manager:3
, so essentially each data collection also triggers a full GC collection cycle.In the graph below is the used memory for the frontend only. The x-axis is the pass index and the y-axis is memory allocation in megabytes.
What we can see here:
Indeed, the situation looks like this:
Note that
Frontend
(as pass manager) was responsible for almost 2/3 of the memory that was not collected.And indeed, the pass manager is pretty hostile to GC:
I sprinkled some finalizers on
P4Program
and it looks like bunch of those are only collected together withFrontend
. And this is suspicious as these are almost never held in the internal state of the passes besides variousProgramMap
's which is mostlyTypeMap
as of now.The particular "leak" pattern looks like this (this is with additional tracing for clones and collection added):
So, what happens here:
DoDefaultArguments
did something as a result new<P4Program>(12469767)
was cloned and clone was not collected as being unused.TypeInference
also inserted some real casts and therefore the P4Program was cloned again into<P4Program>(13001472)
The problem here is that
<P4Program>(12469767)
is only collected when Frontend dies, so it's pointer is being stored somewhere. However, I was unable to track this down so far:TypeMap
holds pointer toP4Program
, however, it was already updated to a new one.TypeInfenceBase
indeed holds the pointer of top-level node (initialNode
), but clearing it inend_apply
did not change anything.The text was updated successfully, but these errors were encountered: