[perf-improver] Replace Array.IndexOf(GetType()) with 'is' pattern matching in hot paths#9299
Draft
Evangelink wants to merge 1 commit into
Draft
Conversation
…hot paths Replace three call sites that used GetType() + Array.IndexOf() for test outcome classification with direct 'is' type-pattern matching. This eliminates repeated array scans and boxing/reflection overhead on the per-test hot path. Changes: - TestApplicationResult.ConsumeAsync: merge _failedTestsCount and _totalRanTests increments directly into the existing switch arms; remove Type outcomeType = executionState.GetType() and three Array.IndexOf calls that duplicated work already done by the switch. - AbortForMaxFailedTestsExtension.ConsumeAsync: replace Array.IndexOf(WellKnownTestNodeTestRunOutcomeFailedProperties, GetType()) with an 'is' pattern chain. - RetryDataConsumer.ConsumeAsync: replace both Array.IndexOf calls with 'is' pattern chains. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR removes redundant GetType() + Array.IndexOf(...) outcome classification in several high-frequency data consumers, replacing it with direct type-pattern matching and folding counter updates into existing switch dispatch. The intent is to reduce per-test overhead on hot paths in Microsoft.Testing.Platform and its Retry extension.
Changes:
- Fold
_failedTestsCount/_totalRanTestsupdates directly into the existingswitch (executionState)inTestApplicationResult.ConsumeAsync, eliminating the second classification pass. - Replace
Array.IndexOf(..., state.GetType())withis ... or ...type patterns inAbortForMaxFailedTestsExtensionandRetryDataConsumer. - Preserve the existing “skipped tests do not count as ran” behavior by keeping skipped excluded from
_totalRanTests.
Show a summary per file
| File | Description |
|---|---|
| src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs | Removes post-switch array-scan classification; updates counters in the same switch arms that already handle each state. |
| src/Platform/Microsoft.Testing.Platform/Extensions/AbortForMaxFailedTestsExtension.cs | Replaces failure-type array membership check with explicit type-pattern matching before incrementing fail count / triggering stop policy. |
| src/Platform/Microsoft.Testing.Extensions.Retry/RetryDataConsumer.cs | Replaces outcome/failure array membership checks with explicit type-pattern matching for retry signaling and total test counting. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 0
| #pragma warning disable CS0618, MTP0001 // Type or member is obsolete | ||
| or CancelledTestNodeStateProperty | ||
| #pragma warning restore CS0618, MTP0001 // Type or member is obsolete | ||
| && ++_failCount >= _maxFailedTests.Value && |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal and Rationale
Every test node update passes through
TestApplicationResult.ConsumeAsync— including the commonInProgressTestNodeStatePropertytransitions that fire for every test start/end. The previous code had aswitchstatement that already dispatched on all known types, then re-ranGetType()plus up to threeArray.IndexOfscans (4–5 elements each) to increment counters and classify outcomes. This double-dispatch was pure overhead.Two other hot paths —
AbortForMaxFailedTestsExtension(active when--maximum-failed-testsis set) andRetryDataConsumer(active when retry is on) — had the same pattern.Approach
Replace every
Array.IndexOf(WellKnownTestNodeXxx, state.GetType()) != -1idiom with anis FailedTestNodeStateProperty or ErrorTestNodeStateProperty ...type-pattern chain, which is resolved by the JIT into a sequence ofisinst/type-check instructions with no allocation, no dictionary lookup, and no array traversal.In
TestApplicationResult.ConsumeAsync, the counter increments (_failedTestsCount++,_totalRanTests++) were merged directly into the existingswitcharms, removing the redundant second pass entirely.Files changed:
TestApplicationResult.csGetType()+ 3×Array.IndexOfAbortForMaxFailedTestsExtension.csGetType()+Array.IndexOfwithispatternRetryDataConsumer.csGetType()+Array.IndexOfwithispatternsPerformance Evidence
The change is a mechanical substitution of array-scan classification with JIT-inlined
isinstchecks. For a 10 000-test run:ConsumeAsynccalled ~10 000× — each call boxedGetType()then scanned arrays of 4–5Typeobjects up to 3 times.switchbranch that was already dispatching; no extra allocations.No microbenchmark exists in this repo for this path yet; the improvement is verified by code-path analysis. The
WellKnownTestNodeXxxarrays remain inTestNodeProperties.Categories.csfor documentation and any callers outside this hot path.Trade-offs
Reproducibility
Test Status
Build: ✅ succeeded (0 warnings, 0 errors)
Microsoft.Testing.Platform.UnitTests: ✅ 1220 passed, 0 failedMicrosoft.Testing.Extensions.UnitTests: ✅ 539 passed, 0 failedAdd this agentic workflows to your repo
To install this agentic workflow, run