Skip to content

perf(core): read protected path forms from their cache per entry - #1989

Merged
tw93 merged 1 commit into
tw93:windowsfrom
b84330808:perf/protected-path-form-table
Nov 13, 2026
Merged

tw93 merged 1 commit into
tw93:windowsfrom
b84330808:perf/protected-path-form-table

Conversation

@b84330808

Copy link
Copy Markdown

Summary

mo clean --dry-run sat on "Developer tools" for several minutes on my Windows 11 machine. Almost all of it was the Bun cache row (%USERPROFILE%\.bun\install\cache: 2,444 package folders, about 87k files and folders, 920 MB). Every entry of every walked tree goes through Test-SafePath, and a big part of that check was PowerShell call overhead rather than the comparisons.

Test-MoleProtectedPathForm called Get-MoleProtectedPathForms once per protected entry for every candidate: the UWP Packages root (after a Join-Path), the profile root and each of the 10 $script:ProtectedPaths. Every call was a cache hit, but a Windows PowerShell 5.1 function call costs more than the string comparisons it feeds.

This change reads the forms straight from $script:MoleProtectedPathFormCache and calls Get-MoleProtectedPathForms only on a miss. The Packages root is kept per LOCALAPPDATA value, so Join-Path runs once per value. The lists, the cached forms and the comparisons are unchanged. A changed LOCALAPPDATA or a protected entry added at runtime is a cache miss and is built as before.

Numbers

Windows 11 Pro 25H2, Windows PowerShell 5.1, i7-9700, NVMe SSD, warm file cache. Base is windows at 6bf9772. Base and patched runs alternated.

Test-SafePath per entry, the first 2,000 entries of the Bun cache, whitelist state passed in as the clean walks do, 6 runs each: 1.82-1.83 ms on base, 0.83-0.85 ms patched.

Whole Bun cache row (Clear-MoleAppCache with dry run on), 2 runs each: 271.0 s and 269.2 s on base, 161.7 s and 162.7 s patched. All four print Bun cache (2444 items, 921.7MB dry).

Safety Review

  • Does this change affect cleanup, uninstall, optimize, installer, remove, analyze delete, update, or install behavior? No. It changes only how Test-MoleProtectedPathForm reaches forms it already cached.
  • Does this change affect path validation, protected directories, symlink handling, sudo boundaries, or release/install integrity? It is inside the protected-path check, but every candidate is still compared with the same forms from the same cache, in the same order.

Tests

  • Automated checks (./scripts/check.sh, ./scripts/test.sh)
  • Manual verification (especially for destructive operations or UI flows)

Three new cases in tests/Core.Tests.ps1: a mocked Get-MoleProtectedPathForms is called 0 times on a warm cache (12 times on the base, so that case fails without the change), the Packages rule follows a LOCALAPPDATA change, and a protected entry added after earlier checks is honored.

Pester 5.7.1 on Windows PowerShell 5.1 with the base.ps1 change, before the new cases were added: Protection, Core, Clean, CleanupBoundary, CleanupTargets, AppCacheSafety, DryRun, DryRunEnvironment, PreviewNoWrite and PurgeBoundary, 433 passed and 1 failed. The failure is Clean leaves user data alone.Lists the cache rows it keeps, which fails the same way on the unchanged base on this machine. Core.Tests alone with the new cases: 94 passed. I did not run the full scripts\test.ps1 or pwsh 7.

Safety-related changes

  • None.

Test-SafePath runs once for every entry of every tree a cleanup walks,
and Test-MoleProtectedPathForm called Get-MoleProtectedPathForms once
per protected entry for each of them: the UWP Packages root (after a
Join-Path), the profile root and each of the ten protected system
paths. Every call was a cache hit, but a Windows PowerShell 5.1
function call costs more than the comparisons it feeds, so the check
spent about 1 ms per entry on calls alone. The dry run of a Bun package
cache with about 87,000 entries drops from 283 s to 175 s.

Read the forms straight from the cache and call the builder only on a
miss. The Packages root is kept per LOCALAPPDATA value, so Join-Path
runs once per value. The lists, the cached forms and the comparisons
are unchanged; a changed LOCALAPPDATA or a protected entry added at
runtime is a miss and is built as before. Tests cover both, and a mock
counts the builder calls on a warm cache (12 before, 0 after).
@tw93
tw93 merged commit 334aff0 into tw93:windows Nov 13, 2026
5 checks passed
@tw93

tw93 commented Nov 13, 2026

Copy link
Copy Markdown
Owner

@b84330808 Thanks for the careful measurements. Pester passed on both Windows PowerShell 5.1 and pwsh 7, so it's merged.

Windows CLI installs follow the windows branch, so a plain mo update brings the faster check.

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