Use an eight-byte heap header#12
Closed
charliermarsh wants to merge 3 commits into
Closed
Conversation
36eea6b to
997eaf2
Compare
Merging this PR will not alter performance
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | string_heap[0] |
312.2 ns | 370.6 ns | -15.74% |
| ❌ | Simulation | string_inline[0] |
312.2 ns | 370.6 ns | -15.74% |
| ⚡ | Simulation | heap_layout/traversal[32] |
711.3 µs | 591.9 µs | +20.17% |
| ⚡ | Memory | heap_layout/construction[17] |
784 KB | 656 KB | +19.51% |
| ⚡ | Memory | heap_layout/construction[24] |
896 KB | 768 KB | +16.67% |
| ⚡ | Simulation | lean_inline[64] |
8.8 µs | 7.7 µs | +14.72% |
| ⚡ | Memory | heap_layout/construction[32] |
1,024 KB | 896 KB | +14.29% |
| ⚡ | Simulation | push_str/unique_heap_growth |
3 µs | 2.6 µs | +13.79% |
| ⚡ | Simulation | string_heap[2] |
3.2 µs | 2.9 µs | +11.78% |
| ⚡ | Memory | push_str/static_mutation |
79 B | 71 B | +11.27% |
| ⚡ | Memory | lean_heap[2] |
80 B | 72 B | +11.11% |
| ⚡ | Memory | lean_shared_first[2] |
80 B | 72 B | +11.11% |
| ⚡ | Memory | heap_layout/construction[48] |
1.3 MB | 1.1 MB | +11.11% |
| ⚡ | Memory | lean_inline[8] |
88 B | 80 B | +10% |
Tip
Investigate this regression with the CodSpeed MCP and your agent.
Comparing charlie/compact-heap-header (d66cd5a) with charlie/codspeed-benchmarks (da4b003)
e1f4b65 to
8aff0f0
Compare
fb1d268 to
da4b003
Compare
ddd62d6 to
d66cd5a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Heap-allocated strings currently place an
AtomicUsizereference count andusizecapacity before their data, giving every 64-bit allocation a 16-byte header. For the short owned strings this crate is designed around, that metadata can be as large as the payload and can push an otherwise small allocation into the next allocator size class.Use an
AtomicU32reference count andu32capacity on 64-bit targets, reducing the common header from 16 bytes to 8 without changing the 16-byteLeanString, its inline capacity, or the static representation. Capacities belowu32::MAXuse this compact layout. Theu32::MAXvalue is reserved as a sentinel for a cold wide layout that stores the fullusizecapacity in an additional word before the common header, restoring the existing2^56 - 1capacity contract without making normal allocations larger.Clone and non-final drop continue to access the common reference-count field without inspecting the capacity representation. Capacity lookup performs one compact-field read and a predicted sentinel branch. Reallocation uses the allocator directly while the representation stays compact or wide; crossing the boundary allocates, copies, and frees because the data offset changes. The compact reference counter now atomically refuses to increment past
u32::MAX, so concurrent cloning cannot wrap it.On macOS, vectors of 17- and 24-byte strings moved from 48-byte to 32-byte physical allocations, a 33% reduction. Construction improved by 8.1% and 7.8% for those sizes, while clone/drop improved by roughly 3%. The tested 32-, 48-, and 64-byte payloads remained in the same allocator size classes; their construction and clone/drop results stayed within the experiment's guardrails, and an initially inconsistent traversal result did not reproduce in a clean repeat. The benchmark executable grew by 176 bytes, or about 0.005%.
The fallback paths are covered by forced-wide allocation, compact-to-wide and wide-to-compact reallocation, clone, and final-drop cases. Strict-provenance Miri passes on 32- and 64-bit little- and big-endian targets, and Loom passes the concurrent mutation tests. This remains a draft while CodSpeed reruns against the fallback implementation and while we repeat the physical-allocation measurements with glibc, jemalloc, and mimalloc rather than assuming macOS size classes generalize.
This branch includes the benchmark foundation from #15. The
heap_layouttarget constructs, clones/drops, and traverses 16,384-string vectors at 11, 12, 13, 15, 16, 17, 24, 32, 48, and 64 bytes. Once #15 is merged and has produced amainbaseline, CodSpeed's simulation and memory instruments will provide repeatable CPU and allocation comparisons across these representation boundaries.