perf: reuse owned buffers on dirty normalize and relative fallbacks#46
perf: reuse owned buffers on dirty normalize and relative fallbacks#46hyfdev wants to merge 15 commits into
Conversation
Merging this PR will improve performance by 41%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Simulation | normalize/current_directory_spellings[empty] |
1,112.8 ns | 243.3 ns | ×4.6 |
| ⚡ | Memory | owned_receiver/pathbuf_result |
154 B | 77 B | ×2 |
| ⚡ | Memory | owned_receiver/string_result |
154 B | 77 B | ×2 |
| ⚡ | Memory | relative_dirty |
149 B | 76 B | +96.05% |
| ⚡ | Simulation | owned_receiver/pathbuf_result |
1,276.1 ns | 996.4 ns | +28.07% |
| ⚡ | Simulation | normalize/current_directory_spellings[dot] |
439.2 ns | 343.9 ns | +27.71% |
| ⚡ | Simulation | normalize/current_directory_spellings[dot_separator] |
440 ns | 344.7 ns | +27.64% |
| ⚡ | Simulation | non_utf8_lexically_clean |
1,010 ns | 855.8 ns | +18.01% |
| ⚡ | Simulation | normalize/clean_rolldown[module_loader_hot] |
1,057.2 ns | 903.6 ns | +17% |
| ⚡ | Simulation | normalize/leading_parent_prescan[clean_68b] |
1,100 ns | 946.4 ns | +16.23% |
| ⚡ | Simulation | owned_receiver/pathbuf_result |
3.4 µs | 2.9 µs | +15.62% |
| ⚡ | Simulation | normalize/clean_rolldown[fixture_p50] |
1,079.2 ns | 954.7 ns | +13.03% |
| ⚡ | Simulation | string |
255.8 ns | 226.7 ns | +12.87% |
| ⚡ | Simulation | borrowed_receiver/pathbuf_result |
1.7 µs | 1.5 µs | +12.3% |
| ⚡ | Simulation | normalize/clean_rolldown[short_source] |
974.2 ns | 878.9 ns | +10.84% |
| ⚡ | Simulation | normalize/needs_work[trailing_separator] |
995.6 ns | 900.3 ns | +10.58% |
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 opt/general-path-sweep (691a4fd) with main (c387d72)2
Footnotes
-
38 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
-
No successful run was found on
main(2c2b185) during the generation of this report, so c387d72 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
There was a problem hiding this comment.
Pull request overview
This PR continues the crate’s performance work on general-path operations by reducing heap allocations in owned-path normalization and relative-path fallbacks, and by updating allocation baselines/docs to reflect the improved call counts.
Changes:
- Reworks owned
PathBufnormalization to rebuild “dirty” paths into the original buffer using a small stack arena (with a fallback to the existingnormalize_innerpath for longer/complex cases). - Adjusts
relativeslow-path plumbing to retainCow<Path>longer, avoiding unnecessary.into_owned()clones in absolute fallbacks. - Adds pre-reserve in
absolutize_withfor the appended relative suffix and updates allocation snapshot gates + performance strategy doc.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/impl_sugar_path.rs | Introduces owned-buffer reuse for dirty normalize; keeps Cow in relative fallbacks; reserves capacity in absolutize_with. |
| benchmarks/allocations/x86_64-unknown-linux-gnu.snap | Updates allocation hard-gate snapshot rows to reflect reduced allocations. |
| benchmarks/allocations/x86_64-pc-windows-msvc.snap | Updates allocation hard-gate snapshot rows to reflect reduced allocations (including Windows-specific rows). |
| .agents/docs/performance-strategy.md | Records the newly achieved allocation reductions and clarifies remaining optimization targets. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Rewrite short dirty into_normalized results into the existing PathBuf storage, so dirty join and absolutize_with drop an intermediate alloc. Keep Cow on the absolute relative fallback so clean bases are not cloned against invalid-encoding targets. Update allocation hard-gate call counts for the intentional rows.
Pass Cow into relative_from_resolved so Windows different-prefix and unrepresentable-suffix returns use into_owned (free on Cow::Owned). Update hard-gate rows for owned invalid into_normalized (0) and Windows root-relative relative (1).
…w ends Avoid moving PathBuf while path.components() is live; set a flag and fall back to normalize_inner only after the iterator is dropped.
Adversarial review flagged the dual dirty-normalize algorithms with only three fixtures. Expand the corpus and add overflow-fallback cases (depth > 24 and length > 512) that must match borrowed normalize.
Document the std encoding contract and that only as_encoded_bytes plus ASCII separators/dots feed the unchecked OsString constructors.
d5cfb81 to
bba4bfc
Compare
64f4f7f to
691a4fd
Compare
Summary
Sweep of remaining general-path allocation headroom after #44:
PathBufallocation through a stack component arena. On non-Unix targets, complete native-encoding fragments are held by a branded append-only arena and written through safeOsStringoperations; Unix rebuilds use safeOsStringExtAPIs.Cow<Path>instead of forcing a clean base into an owned buffer.PathBuf::pushforabsolutize_withgrowth and cover exact-capacity, 24/25/33-component, bounded replay, and long Windows UNC boundaries.mainwhile compiling the consuming owned path independently; address-dependent layout may differ.Hard-gate allocation moves
into_normalizedabsolutize_withrelative + borrowed cwdabsolutize_withrelative + owned cwdrelativeClean-path zero-allocation gates remain unchanged. Unequal relative inputs remain at one allocation from #44.
Boundaries
main's opcode sequence and out-of-line call boundary; address-dependent layout and immediates may differ. The consuming normalizer intentionally uses the new owned-buffer fast path.OsStr::as_encoded_bytes()values; callers only use safe APIs.Intentionally not changed
normalizenormalize().into_owned()Test plan
cargo test --locked --workspace --all-features-D warningsRUSTDOCFLAGS=-D warnings