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

Reduce memory usage by interning Tasks and RuleGraph entries. #14683

Merged
merged 2 commits into from
Mar 3, 2022

Conversation

stuhood
Copy link
Member

@stuhood stuhood commented Mar 3, 2022

The --stats-memory-summary added in #14638/#14652 was reporting surprisingly large sizes for native NodeKey structs -- even when excluding the actual Python values that they held. Investigation showed that both the Task and Entry structs were contributing significantly to the size of the Task struct.

The internment crate used here (and in #14654) is an alternative to giving these values integer IDs. They become pointers to a unique, shared (technically: leaked) copy of the value. They are consequently 1) much smaller, 2) much faster to compare.

The top-reported memory usage of ./pants dependencies --transitive :::

[ci skip-build-wheels]

@stuhood stuhood added this to the 2.10.x milestone Mar 3, 2022
Copy link
Contributor

@Eric-Arellano Eric-Arellano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙌

@stuhood stuhood enabled auto-merge (squash) March 3, 2022 16:55
@tdyas
Copy link
Contributor

tdyas commented Mar 3, 2022

They become pointers to a unique, shared (technically: leaked) copy of the value.

Will these leaks ever cause memory problems?

@stuhood
Copy link
Member Author

stuhood commented Mar 3, 2022

They become pointers to a unique, shared (technically: leaked) copy of the value.

Will these leaks ever cause memory problems?

No, I believe that this is a very safe case.

We're interning Task structures, which are created from @rules. Unless you are dynamically defining new @rules inside of a loop, any given process has a bounded set of Tasks that will be created (even across Scheduler instances).

But! That is why it wouldn't be a good idea to intern the entire NodeKey struct: it contains user-defined, per-execution values.

@stuhood stuhood merged commit 312ab8b into pantsbuild:main Mar 3, 2022
@stuhood stuhood deleted the stuhood/node-key-sizes branch March 3, 2022 18:06
@Eric-Arellano
Copy link
Contributor

We're interning Task structures, which are created from @rules. Unless you are dynamically defining new @rules inside of a loop, any given process has a bounded set of Tasks that will be created (even across Scheduler instances).

But! That is why it wouldn't be a good idea to intern the entire NodeKey struct: it contains user-defined, per-execution values.

This would be good to memorialize in comments.

stuhood added a commit to stuhood/pants that referenced this pull request Mar 3, 2022
…uild#14683)

The `--stats-memory-summary` added in pantsbuild#14638/pantsbuild#14652 was [reporting surprisingly large sizes](pantsbuild#12662 (comment))  for native `NodeKey` structs -- even when excluding the actual Python values that they held. Investigation showed that both the `Task` and `Entry` structs were contributing significantly to the size of the `Task` struct.

The [`internment` crate](https://crates.io/crates/internment) used here (and in pantsbuild#14654) is an alternative to giving these values integer IDs. They become pointers to a unique, shared (technically: leaked) copy of the value. They are consequently 1) much smaller, 2) much faster to compare.

The `top`-reported memory usage of `./pants dependencies --transitive ::`:
* `313M` before (summary [before.txt](https://github.com/pantsbuild/pants/files/8175461/before.txt))
* `220M` after (summary [after.txt](https://github.com/pantsbuild/pants/files/8175462/after.txt))

[ci skip-build-wheels]
stuhood added a commit that referenced this pull request Mar 3, 2022
…pick of #14683) (#14689)

The `--stats-memory-summary` added in #14638/#14652 was [reporting surprisingly large sizes](#12662 (comment))  for native `NodeKey` structs -- even when excluding the actual Python values that they held. Investigation showed that both the `Task` and `Entry` structs were contributing significantly to the size of the `Task` struct.

The [`internment` crate](https://crates.io/crates/internment) used here (and in #14654) is an alternative to giving these values integer IDs. They become pointers to a unique, shared (technically: leaked) copy of the value. They are consequently 1) much smaller, 2) much faster to compare.

The `top`-reported memory usage of `./pants dependencies --transitive ::`:
* `313M` before (summary [before.txt](https://github.com/pantsbuild/pants/files/8175461/before.txt))
* `220M` after (summary [after.txt](https://github.com/pantsbuild/pants/files/8175462/after.txt))

[ci skip-build-wheels]
stuhood added a commit that referenced this pull request Mar 20, 2022
…ased on the level (#14854)

This change cleans up a few TODOs around workunit storage.

Workunit names are almost entirely `&'static str`, with the notable exception of `NodeKey::Task` names (which are the Python function names of `@rules`). But #14683 began interning `Task` structs, which made `Task` names static as well. This change moves to storing workunit names as `&'static str`.

Additionally, because the `Level` is a field of the `WorkunitMetadata`, the `WorkunitMetadata` was forced to be constructed, even for workunits whose level meant that they would not be rendered. Since the `WorkunitMetadata` contains heavy fields like the string description and user-metadata, this change moves to constructing the `WorkunitMetadata` via lazily evaluated `key=value` arguments to the `in_workunit!` macro (which also has the advantage of being more concise).

Finally: an unused argument to `in_workunit!` is removed.

Speeds up common runs by ~2%.

[ci skip-build-wheels]
stuhood added a commit to stuhood/pants that referenced this pull request Mar 29, 2022
…ased on the level (pantsbuild#14854)

This change cleans up a few TODOs around workunit storage.

Workunit names are almost entirely `&'static str`, with the notable exception of `NodeKey::Task` names (which are the Python function names of `@rules`). But pantsbuild#14683 began interning `Task` structs, which made `Task` names static as well. This change moves to storing workunit names as `&'static str`.

Additionally, because the `Level` is a field of the `WorkunitMetadata`, the `WorkunitMetadata` was forced to be constructed, even for workunits whose level meant that they would not be rendered. Since the `WorkunitMetadata` contains heavy fields like the string description and user-metadata, this change moves to constructing the `WorkunitMetadata` via lazily evaluated `key=value` arguments to the `in_workunit!` macro (which also has the advantage of being more concise).

Finally: an unused argument to `in_workunit!` is removed.

Speeds up common runs by ~2%.

[ci skip-build-wheels]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants