Preallocate Rust sets when extracting Python sets#6225
Conversation
Merging this PR will improve performance by 17.83%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | extract_hashset |
23.6 ms | 19.1 ms | +23.52% |
| ⚡ | extract_hashbrown_set |
19.1 ms | 17 ms | +12.41% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing charliermarsh:charlie/preallocate-python-set-extraction (474fe00) with main (2e49529)
|
|
|
I could be wrong, but AFAICT, this ultimately calls |
|
Is this a GitHub bug? I got the same message three times |
Ugh, sorry, no -- I'm on airplane Wi-Fi and I think it re-sent a bunch of times! |
FYI our PyList iterator is also buggy (see #6087), so I'm inclined to allow this. Or find a solution for both list and sets. |
|
@charliermarsh Glad to merge if you think the comments have been addressed |
|
Just marked comments as resolved, thanks @Tpt! |
|
The CI failure is unrelated: ruff (😉) now tries to reformat code snippets in markdown and we have not pinned its version. Wil lfix tomorrow |
It seems worth pinning all versions of the tools in the dependency groups, including zizmor, typos, etc. |
When extracting a Python
setorfrozensetinto a RustHashSet, we currently collect a fallible iterator. Collecting intoResultloses the lower bound from the source iterator's size hint, so the set grows repeatedly even though its exact length is known. In other words, because it's fallible, Rust can't pre-size the collection.This PR instead preallocates from the Python set's length before extracting and inserting each element. (This matches the existing pattern used for for
Vec,SmallVec, and Python dictionaries intoHashMap, all of which already reserve capacity from the Python collection's length.)For a 100,000-element set, this reduces allocations from 16 to 1 and cumulative Rust allocation traffic from 2,359,388 bytes to 1,179,656 bytes.