Skip to content

Skip unused work in discovery post-processing and output#2918

Open
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-perf-discovery-folds
Open

Skip unused work in discovery post-processing and output#2918
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-perf-discovery-folds

Conversation

@nohwnd

@nohwnd nohwnd commented Jul 19, 2026

Copy link
Copy Markdown
Member

Three changes on the discovery and post-processing side:

  • The WriteScreen plugin flattened the whole discovered tree with View-Flat on DiscoveryEnd, but the result is only read when Run.SkipRun is set or verbosity is Detailed/Diagnostic. In the default run it was computed and thrown away, the cost grows with the number of discovered tests. It is now computed only when one of those two guarded blocks can actually read it.
  • The RSpec decoration walk (Add-RSpecTestObjectProperties per test, run once per container after it finishes) and Remove-RSpecNonPublicProperties used Fold-Container/Fold-Run, which pay a scriptblock dispatch per item plus an advanced-function recursion per block. Both are now direct recursive loops that visit the same items in the same pre-order (tests of a block before its child blocks). The decoration body itself is unchanged and stays in one place, the walker just calls it.
  • Format-Nicely2 called the Is-DecimalNumber, Is-ScriptBlock, Is-Collection and Is-Value helpers on every formatted value, and it runs for every <template> name expansion of data-driven tests. The type checks are inlined (Is-Collection is the same LanguagePrimitives::GetEnumerator test), the helpers stay defined for their other callers.

Verification: full suite green on macOS (inline and dot-sourced build), Pester.RSpec.ts.ps1, Pester.RSpec.Parallel.ts.ps1 (covers the parallel decoration path, which changed too) and Pester.RSpec.Output.ts.ps1 pass in fresh processes. Result object shape is covered by Pester.RSpec.ResultObject.ts.ps1, also green.

Related perf PRs: #2914, #2915, #2916, #2917. Measured together they cut ~21% from four representative workloads (inline build, macOS).

🤖

The WriteScreen plugin flattened the whole discovered tree with View-Flat
on DiscoveryEnd, but the result is only read when Run.SkipRun is set or
verbosity is Detailed/Diagnostic, in the default run it was computed and
thrown away. It is now computed only when one of those consumers can read
it.

The RSpec decoration walk (Add-RSpecTestObjectProperties per test) and
Remove-RSpecNonPublicProperties used Fold-Container/Fold-Run, which pay a
scriptblock dispatch per item and an advanced-function recursion per block.
Both are now direct recursive loops that visit the same items in the same
pre-order.

Format-Nicely2 called Is-DecimalNumber, Is-ScriptBlock, Is-Collection and
Is-Value helpers on every formatted value, on the dispatch path used for
every <template> name expansion. The type checks are inlined, the helpers
stay defined for other callers.

🤖
Comment thread src/Pester.RSpec.ps1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just curious so I can learn, what's the big benefit here besides clearer names? We skip maybe 1-2 light function calls, but all the scriptblocks are reference types and passed on from the initial call.

Comment thread src/Format2.ps1
if (Is-DecimalNumber -Value $Value) {
# Inlined Is-DecimalNumber / Is-ScriptBlock to avoid a function call per formatted value on this
# hot dispatch path (used for every <template> name expansion). The helpers stay defined for other callers.
if ($Value -is [float] -or $Value -is [single] -or $Value -is [double] -or $Value -is [decimal]) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is-DecimalNumber is only used here and Format.ps1. Maybe update both?

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