fix(linter): OOM problems with custom plugins#17013
fix(linter): OOM problems with custom plugins#17013overlookmotel merged 6 commits intographite-base/17013from
Conversation
CodSpeed Performance ReportMerging #17013 will not alter performanceComparing Summary
Footnotes
|
dcc634d to
eec6e24
Compare
c6cc9c9 to
bd52e47
Compare
e646067 to
47489d4
Compare
|
IMO we should engage the "copy approach" whenever both multi-file linting and JS plugins are both in use, regardless of the file count. Because:
Maybe that's overkill, but I think it's better to be on the safe side, given how catastrophic it going wrong can be on Mac. |
c58b1c4 to
c45c03e
Compare
b5af75c to
63dc533
Compare
👍 agreed |
a96406c to
36eb52a
Compare
c45c03e to
907ddd8
Compare
7cf0389 to
35c0e76
Compare
540bcb4 to
361ca74
Compare
There was a problem hiding this comment.
Looks good!
I've pushed a few commits with minor refactoring and editing comments. Will manually merge so Github retains the individual commits I pushed in case you want to check them (I did the same on the 2 other PRs in the stack).
Will follow up with more substantive changes in separate PRs.
|
Oh goddamn! I merged this PR, but I merged it into the Graphite temp branch, not main. Ergh. Have revived the branch and opened a new PR - #17082. Will merge that now, into main this time. |
Follow-on after #17013. Pure refactor. Move logic for getting `AllocatorPool` into a function `js_allocator_pool`, to avoid lots of `#[cfg]` stuff.
Follow-on after #17013. Refactor. Reduce repeated code by getting a `&mut Program` out of the `ContextHost` at the start of the process, instead of in 2 places later on. This also enables an optimization in next PR.
…ize allocator (#17088) Follow-on after #17013. We already copy the source text into the new fixed-sized allocator. Prevent the source also being cloned by `program.clone_in(allocator)` - that's unnecessary as we immediately overwrite it. This is enabled by #17087, which allows us to have a `&mut Program` at this point.
…17094) Modification of fixed-size allocator limits, building on #17023. ### The problem This is an alternative design, intended to handle one flaw on Windows: Each allocator is 4 GiB in size, so if system has 16.01 GiB of memory available, we could succeed in creating 4 x 4 GiB allocators, but that'd only leave 10 MiB of memory free. Likely then some other allocation (e.g. creating a normal `Allocator`, or even allocating a heap `String`) would fail due to OOM later on. Note that "memory available" on Windows does not mean "how much RAM the system has". It includes the swap file, the size of which depends on how much free disk space the system has. So numbers like 16.01 GiB are not at all out of the question. ### Proposed solution On Windows, create as many allocators as possible when creating the pool, up to `thread count + 1`. Then return the last allocator back to the system. This ensures that there's at least 4 GiB of memory free for other allocations, which should be enough. ### Redesign In working through the various scenarios, I realized that the implementation can be simplified for both Linux/Mac and Windows. In both cases, no more than `thread_count` fixed-size allocators can be in use at any given time - see doc comment on `FixedSizeAllocatorPool` for full explanation. So create the pool with `thread_count` allocators (or as close as we can get on Windows). Thereafter the pool does not need to grow, and cannot. This allows removing a bunch of synchronization code. * On Linux/Mac, #17013 solved the too-many-allocators problem another way, so all we need is the `Mutex`. * On Windows, we only need a `Mutex` + a `Condvar`. In both cases, it's much simplified, which makes it much less likely for subtle race conditions like #17112 to creep in. Removing the additional synchronization should also be a little more performant. Note that the redesign is not the main motivator for this change - preventing OOM on Windows is.

This PR fixes OOM (Out of Memory) problems when using JS plugins together with the import plugin for cross-module analysis.
Previously, a stopgap solution simply refused to run when >10,000 files were linted with both features enabled.
This PR implements a real fix using a deferred cloning strategy: standard allocators are used for parsing/linting (efficient, dynamic sizing), and the AST is only copied into a fixed-size allocator briefly when calling JS plugins.
This dramatically reduces memory usage from n_files × 6 GiB to n_files × dynamic + thread_count × 6 GiB.