Skip to content

Reduce PowerShell module build analysis and formatting overhead - #1024

Merged
PrzemyslawKlys merged 5 commits into
mainfrom
fix/module-build-performance
Oct 4, 2026
Merged

PrzemyslawKlys merged 5 commits into
mainfrom
fix/module-build-performance

Conversation

@PrzemyslawKlys

@PrzemyslawKlys PrzemyslawKlys commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

PowerShell module builds repeatedly bind the same commands during analysis and reload PowerShell/PSScriptAnalyzer across formatting groups and phases. Cache successful bindings within an analysis, restrict execution-graph candidates to their actual function scope, skip disabled preprocessing, and batch each tree's file groups. The build now owns one isolated formatter host across staging and project formatting, then disposes it before later actions and signing.

Each group and phase retains its settings, working directory, deadline, and per-file results. Completed errors remain errors when a later file times out; the interrupted host is discarded before another request. A staging failure stops project formatting. Normalization writes only when target bytes differ, reports encoding-only changes correctly, and preserves timestamps for already-normalized files. The implementation stays in PowerForge and PowerForge.PowerShell, with no public API or production dependency changes.

Related consumers are Locksmith #299 and Locksmith2 #111. Their public minimum remains 3.0.153. Publish a PSGallery version containing the owner changes before raising that minimum.

Validation includes 38 focused formatter/normalization contracts, including six child-process lifecycle cases; zero-warning builds for net472/net8.0/net10.0; real PSScriptAnalyzer output equivalence with different settings, Unicode, and casing on PowerShell 7.6.6 and Windows PowerShell 5.1; and real Build-Module staging-failure checks on both hosts. Independent local review covers the analysis/batching, normalization, and added host-lifetime contracts with no unresolved findings. Earlier broader module validation and the packed 3.0 script path have separate proof; broad unfiltered engine validation is not claimed complete.

The latest full comparison uses 2.0.27, the preceding implementation at 2dc7cd2, and the formatter-reuse production files committed at 8e901ef. Both cache domains use fixed affinity, normal priority, one warm-up and three rotated measured builds per engine/module, identical cached dependencies, project formatting enabled, and no outlier removal.

Build Affinity 2.0.27 median Before reuse median With reuse median
Locksmith, Script only FFFF 22.06 s 33.97 s 33.49 s
Locksmith, Script only FFFF0000 15.26 s 25.45 s 25.22 s
Locksmith2, full build FFFF 18.25 s 18.08 s 23.26 s
Locksmith2, full build FFFF0000 12.96 s 17.58 s 14.37 s

Whole-build results are mixed: Locksmith2 improves about 18% on the second domain, while its first-domain median worsens. Ranges remain wide; the first domain contains approximately 118-second samples in both the legacy and reused lanes. A nearby snapshot shows a shared Roslyn compiler consuming roughly 16 CPU-core equivalents, which limits attribution. No formatter timeout appears in these build logs. This comparison does not establish a consistent whole-build improvement or a win over 2.0.

Separate phase diagnostics show project formatting falling from 0.702 to 0.055 seconds for Locksmith and 1.911 to 0.180 seconds for Locksmith2. These are individual diagnostic runs, not benchmark medians; staging formatting remains the dominant cost.

All 36 measured artifacts pass structural checks. The before/reused 3.0 artifacts import on both runtimes with 1 and 9 exports, and all five compared output files are byte-identical on each domain. Legacy Locksmith imports on Desktop; its Core import is blocked by the older ServerManager manifest requirement on this workstation. Locksmith uses Script-only for all engines because the tested 2.0 packed path does not produce a valid ZIP. Runs 20261004-174939-d518ff89 and 20261004-181423-03e6d6a7 retain every measured sample; incomplete dependency-cache attempts are excluded.

Native private packages 3.0.155.6/3.0.155.7 are validation artifacts only. The candidate package was built before committing its reviewed production files, so its embedded Git metadata may retain the preceding SHA; native DLL fingerprints are retained. This PR is separate from package publication and consumer release qualification.

@PrzemyslawKlys
PrzemyslawKlys merged commit b37cb2c into main Oct 4, 2026
15 of 16 checks passed
@PrzemyslawKlys
PrzemyslawKlys deleted the fix/module-build-performance branch October 4, 2026 21:35
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