Skip to content

perf: reduce redundant scans in path hot paths#47

Merged
hyfdev merged 7 commits into
mainfrom
agent/safe-path-hot-paths
Jul 13, 2026
Merged

perf: reduce redundant scans in path hot paths#47
hyfdev merged 7 commits into
mainfrom
agent/safe-path-hot-paths

Conversation

@hyfdev

@hyfdev hyfdev commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Status

Draft — waiting for baseline #48 to land before retargeting. The three known regressions are all explicitly non-UTF-8 and are not performance acceptance gates under the vouched project rule. This PR is independent of #46.

Summary

  • classify path spelling from OsStr::as_encoded_bytes() on conventional Unix and Windows; keep Cygwin on the existing Unicode-validation gate
  • retain Cow<Path> through relative fallback resolution so clean normalized intermediates are cloned only when the final result must own them
  • reuse owned Windows slash-form relative buffers in place with safe byte replacement
  • keep the existing generic Unix relative path, inlineable normalize_inner, and single-pass borrowed slash conversion; the rejected Unix scanner and double-scan conversion are absent

The diff adds no unsafe code.

Performance decision

SugarPath's vouched performance rule treats paths that can be represented losslessly as valid UTF-8 as the default performance target. An optimization remains valid when native non-UTF-8 inputs become slower, provided their exact path data, results, errors, and safety remain correct.

Under that rule, keep the encoded-byte classifier, Cow<Path> propagation, and Windows owned-buffer reuse. Do not restore the Unix fixed-width scanner. Keep every non-UTF-8 benchmark independently visible, but do not add complexity or sacrifice demonstrated UTF-8 gains solely to equalize those rows without consumer evidence.

The recorded Rolldown trace contained 4,901 normalization calls, all clean valid inputs, and no dirty or non-UTF-8 normalization inputs.

Performance evidence

The final stacked CodSpeed comparison reports 14 improvements, 3 regressions, 211 untouched benchmarks, and 36 skipped benchmarks. Every regressed row now identifies non_utf8 directly in its leaf name.

Improvements include:

  • non_utf8_lexically_clean: 2.5 µs to 1.0 µs (×2.4)
  • non_utf8_absolute_target: 8.4 µs to 7.2 µs (+17.33%)
  • the same relative case's peak memory: 149 B to 64 B (×2.3)
  • normalize/owned_input/module_loader_hot/borrowed_receiver/pathbuf_result: +48.25%
  • normalize/rolldown_corpus/clean: +10.44%

The three regressions are:

  • dirty_before_non_utf8: 2.5 µs to 3.0 µs (-16.27%)
  • non_utf8_before_dirty_late: 2.7 µs to 3.2 µs (-16.27%)
  • borrowed_receiver/non_utf8_strict: 441.1 ns to 499.4 ns (-11.68%)

The Unix implementation exercised by borrowed_receiver/non_utf8_strict remains the same Path::to_str().map(Cow::Borrowed) path between BASE and HEAD, so that result reflects whole-crate compiled layout rather than added slash-conversion work.

CodSpeed warns that BASE and HEAD used different runtime environments. The three regression values and the principal classifier results reproduced across consecutive runs, so the rows remain useful directional evidence while exact gains should not be treated as universal constants.

The original unacceptable regressions are gone: no valid-UTF-8 relative row regresses by 10%–26%, the parent-cancellation regression from forcing normalize_inner out of line is gone, and the valid-UTF-8 to_slash regression from scanning twice is gone.

Native Linux and Windows allocation checks both pass with no allocation-count regression. Clean non-UTF-8 normalization changes from one allocation to zero, owned non-UTF-8 consuming normalization changes from one to zero, and non-UTF-8 absolute relative changes from three allocations to one. Windows also reduces dotted relative from four to three allocations, absolute forward-slash relative from two to one, and root-relative from three to one.

Release cargo llvm-lines changes from 16,682 to 16,665 lines and from 431 to 430 copies, so the retained implementation does not add the code-size increase from the removed scanner.

Correctness and safety

  • no new unsafe; all new implementation code uses safe Cow, encoded-byte inspection, and owned-buffer mutation
  • exhaustive short-input classifier tests compare dispatch with unconditional normalization for arbitrary Unix bytes and Windows wide units
  • Cygwin keeps the previous Unicode-validation behavior; the new non-UTF-8 borrowing expectation is explicitly excluded there
  • non-UTF-8 native paths have explicit borrow-versus-rebuild, strict-conversion, lossy-conversion, and relative-path preservation tests
  • Windows owned separator replacement checks exact Unicode output and pointer/capacity reuse

Validation

  • cargo fmt --all --check
  • cargo test --locked --workspace --all-features
  • cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
  • RUSTDOCFLAGS="-D warnings" cargo doc --locked --workspace --all-features --no-deps
  • cargo bench --locked --no-run
  • library checks for x86_64-unknown-uefi, x86_64-pc-windows-gnu, and x86_64-pc-windows-msvc
  • GitHub Actions tests on Ubuntu, macOS, and Windows
  • native Linux and Windows allocation snapshot checks
  • CodSpeed simulation and memory benchmark jobs

The CodSpeed summary remains red and has not been acknowledged, so all three non-UTF-8 regressions stay visible while this PR is Draft.

Draft gates

@codspeed-hq

codspeed-hq Bot commented Jul 13, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 20.78%

⚠️ 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

⚡ 13 improved benchmarks
❌ 3 (👁 2) regressed benchmarks
✅ 212 untouched benchmarks
⏩ 38 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation owned_receiver/string_result 401.7 ns 460 ns -12.68%
Simulation non_utf8_lexically_clean 2.5 µs 1 µs ×2.4
Memory non_utf8_absolute_target 149 B 64 B ×2.3
Simulation borrowed_receiver/pathbuf_result 2.5 µs 1.7 µs +45.76%
Simulation normalize/current_directory_spellings[dot] 534.4 ns 439.2 ns +21.7%
Simulation clean_absolute 1.3 µs 1.1 µs +18.06%
Simulation clean_absolute 1.4 µs 1.2 µs +16.65%
Simulation normalize/canonical_leading_parents[leading_parents] 1,088.6 ns 935 ns +16.43%
Simulation non_utf8_absolute_target 8.4 µs 7.2 µs +16.39%
Simulation normalize/current_directory_spellings[dot_separator] 507.2 ns 440 ns +15.28%
Simulation normalize/leading_parent_prescan[clean_68b] 1.3 µs 1.1 µs +13.68%
Simulation string 255.8 ns 226.7 ns +12.87%
Simulation clean 3.3 µs 3 µs +10.44%
Simulation normalize/clean_rolldown[fixture_p50] 1.2 µs 1.1 µs +10.08%
👁 Simulation non_utf8_before_dirty_late 2.7 µs 3.2 µs -16.27%
👁 Simulation non_utf8_dirty_before 2.5 µs 3 µs -16.27%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing agent/safe-path-hot-paths (1308526) with main (606e366)

Open in CodSpeed

Footnotes

  1. 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.

@hyfdev hyfdev force-pushed the agent/safe-path-hot-paths branch from 998dfdd to 965f3d3 Compare July 13, 2026 06:00
@hyfdev hyfdev changed the base branch from main to agent/safe-path-hot-path-baselines July 13, 2026 06:00
@hyfdev hyfdev force-pushed the agent/safe-path-hot-path-baselines branch from c76bdbb to d2ed128 Compare July 13, 2026 06:04
@hyfdev hyfdev force-pushed the agent/safe-path-hot-paths branch from 965f3d3 to e16f1fb Compare July 13, 2026 06:05
@hyfdev hyfdev force-pushed the agent/safe-path-hot-path-baselines branch from d2ed128 to 500497a Compare July 13, 2026 06:07
@hyfdev hyfdev force-pushed the agent/safe-path-hot-paths branch 7 times, most recently from dff41c7 to f2dc484 Compare July 13, 2026 07:31
@hyfdev hyfdev changed the base branch from agent/safe-path-hot-path-baselines to main July 13, 2026 07:31
@hyfdev hyfdev force-pushed the agent/safe-path-hot-paths branch from f2dc484 to 2381636 Compare July 13, 2026 09:55
@hyfdev hyfdev changed the base branch from main to agent/non-utf8-benchmark-prefix July 13, 2026 09:55
@hyfdev hyfdev force-pushed the agent/safe-path-hot-paths branch from 2381636 to f99b32c Compare July 13, 2026 10:01
@hyfdev hyfdev changed the base branch from agent/non-utf8-benchmark-prefix to main July 13, 2026 10:01
@hyfdev hyfdev marked this pull request as ready for review July 13, 2026 10:02
Copilot AI review requested due to automatic review settings July 13, 2026 10:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes SugarPath’s path hot paths by reducing redundant scans and unnecessary allocations, especially around normalization classification and Windows relative-path separator handling, while explicitly keeping correctness for non-UTF-8/native-encoding inputs.

Changes:

  • Switch normalization “needs work?” classification to inspect OsStr::as_encoded_bytes() on conventional Unix/Windows (keeping Cygwin on the Unicode-validation path) and add exhaustive classifier-vs-full-normalizer tests.
  • Preserve Cow<Path> through relative-path resolution so clean intermediates remain borrowed unless an owned result is required.
  • Reuse owned Windows slash-form relative buffers by replacing /\ in-place (no extra allocation) and update invalid-encoding tests + allocation snapshots accordingly.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/impl_sugar_path.rs Implements encoded-byte normalization classifier changes, retains Cow<Path> in relative fallbacks, and adds in-place Windows separator replacement + tests.
tests/invalid_encoding.rs Adds coverage ensuring clean invalid-encoding inputs can borrow while dirty spellings rebuild, on Unix (non-Cygwin) and Windows.
benchmarks/allocations/x86_64-unknown-linux-gnu.snap Updates allocation baselines reflecting reduced allocations for invalid-encoding normalization/relative cases.
benchmarks/allocations/x86_64-pc-windows-msvc.snap Updates allocation baselines reflecting reduced allocations for invalid-encoding normalization/relative and Windows separator-handling cases.
.agents/docs/performance-strategy.md Documents the new classifier strategy, tradeoffs, and supporting benchmark evidence/constraints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/impl_sugar_path.rs
@hyfdev hyfdev merged commit 886fc35 into main Jul 13, 2026
8 of 9 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 13, 2026
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.

2 participants