Skip to content

Use an eight-byte heap header#12

Closed
charliermarsh wants to merge 3 commits into
charlie/codspeed-benchmarksfrom
charlie/compact-heap-header
Closed

Use an eight-byte heap header#12
charliermarsh wants to merge 3 commits into
charlie/codspeed-benchmarksfrom
charlie/compact-heap-header

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Jul 6, 2026

Copy link
Copy Markdown
Member

Heap-allocated strings currently place an AtomicUsize reference count and usize capacity 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 AtomicU32 reference count and u32 capacity on 64-bit targets, reducing the common header from 16 bytes to 8 without changing the 16-byte LeanString, its inline capacity, or the static representation. Capacities below u32::MAX use this compact layout. The u32::MAX value is reserved as a sentinel for a cold wide layout that stores the full usize capacity in an additional word before the common header, restoring the existing 2^56 - 1 capacity 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_layout target 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 a main baseline, CodSpeed's simulation and memory instruments will provide repeatable CPU and allocation comparisons across these representation boundaries.

@charliermarsh charliermarsh force-pushed the charlie/compact-heap-header branch 3 times, most recently from 36eea6b to 997eaf2 Compare July 7, 2026 12:36
@codspeed-hq

codspeed-hq Bot commented Jul 7, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 12 improved benchmarks
❌ 2 regressed benchmarks
✅ 130 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

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)

Open in CodSpeed

@charliermarsh charliermarsh changed the base branch from main to charlie/codspeed-benchmarks July 7, 2026 12:43
@charliermarsh charliermarsh force-pushed the charlie/compact-heap-header branch from e1f4b65 to 8aff0f0 Compare July 7, 2026 12:44
@charliermarsh charliermarsh force-pushed the charlie/codspeed-benchmarks branch from fb1d268 to da4b003 Compare July 7, 2026 17:01
@charliermarsh charliermarsh force-pushed the charlie/compact-heap-header branch from ddd62d6 to d66cd5a Compare July 7, 2026 17:05
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.

1 participant