[efficiency-improver] perf: cache ExecutionId and single-pass property scan in DotnetTestDataConsumer#8866
Merged
Evangelink merged 2 commits intoJun 7, 2026
Conversation
…stDataConsumer Previously, DotnetTestDataConsumer had two per-test inefficiencies: 1. ExecutionId was a computed property calling GetEnvironmentVariable() on every access. For N tests, this produced N environment-variable lookups and N redundant string allocations (once per TestNodeUpdateMessage, plus once each for session start/end events). The environment variable is set once at process startup and never changes, so the value is now cached in a readonly field in the constructor. 2. GetTestNodeDetails() walked the PropertyBag linked list separately for each of StandardOutputProperty, StandardErrorProperty, and TimingProperty (3 walks per test). These are replaced with a single pass using PropertyBag.GetStructEnumerator() (a zero-allocation struct enumerator), reducing linked-list traversals from 3N to N for a run with N tests. Proxy metrics (CPU cycles / memory allocation) map to energy reduction: fewer object allocations reduce GC pressure; fewer linked-list walks reduce instruction count. For a 10,000-test run these savings are small but consistent, and they compound across every process that uses dotnet test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR targets per-test execution efficiency in the Dotnet test IPC consumer (DotnetTestDataConsumer) within Microsoft.Testing.Platform by removing redundant environment-variable lookups and reducing repeated property-bag scans.
Changes:
- Cache the dotnet-test
ExecutionIdat construction time instead of recomputing it per message. - Replace multiple
PropertyBag.SingleOrDefault<T>()traversals inGetTestNodeDetailswith a single struct-enumerator pass that gathers required properties.
Show a summary per file
| File | Description |
|---|---|
src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs |
Caches execution id and performs single-pass property extraction for per-test result message creation. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 1
1 task
Keep the single-pass scan but throw the same InvalidOperationException as PropertyBag.SingleOrDefault when duplicate timing/output/error properties are present. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
🤖 This is a draft PR from Efficiency Improver, an automated AI assistant focused on reducing energy consumption and computational footprint.
Goal & Rationale
Two per-test inefficiencies in
DotnetTestDataConsumer(the consumer that ships every test result todotnet test):ExecutionIdwas a computed property — it calledGetEnvironmentVariable()on every single access. The value is set once at process startup and never changes during a test run, yet N test result events each paid the cost of a native environment-variable lookup + a new string allocation. Caching it as areadonlyfield removes all N redundant lookups.GetTestNodeDetailswalked thePropertyBaglinked list 3× per test — once forStandardOutputProperty, once forStandardErrorProperty, and once (conditionally) forTimingProperty. These three separate passes are replaced with a single pass usingPropertyBag.GetStructEnumerator(), a zero-allocation struct enumerator that is internal to theMicrosoft.Testing.Platformassembly.Focus Area
Code-Level Efficiency — unnecessary computation / redundant allocation per test result.
Approach
Change 1 — Cache
ExecutionId_environmentis no longer needed as a field, so it is removed.Change 2 — Single-pass property scan in
GetTestNodeDetailsEnergy Efficiency Evidence
Proxy metric used: CPU instruction count + heap allocation count
(Direct energy measurement unavailable; these proxies are standard Green Software proxies for energy reduction.)
GetEnvironmentVariablecalls / testGetTestNodeDetailsGetTestNodeDetailscallFor a 10 000-test
dotnet testrun:These are small absolute numbers but occur on every test result in every
dotnet testrun. They are consistent, compounding, and require no trade-off in correctness or readability.Green Software Foundation Context
Hardware Efficiency — eliminating redundant work (native calls, string allocations, list traversals) lets the same hardware do more testing per unit of energy. Per the GSF SCI model, reducing instructions/allocations per functional unit (one test result) directly lowers the Energy component.
Trade-offs
SingleOrDefaultcalls, but the trade-off is clearly documented in an inline comment.PropertyBag.PropertyBagEnumeratortype isinternal. This pattern is already used elsewhere in the platform assembly (e.g.SerializerUtilities) and is the canonical low-allocation iteration path within the assembly.TestNodeStatePropertywas already retrieved via the O(1) fast-path field, unchanged.Test Status
✅ Full solution build:
./build.sh -build— Build succeeded. 0 Warning(s). 0 Error(s).Reproducibility
For quantitative comparison, profile with
dotnet-traceor BenchmarkDotNet targetingDotnetTestDataConsumer.ConsumeAsyncwith a syntheticTestNodeUpdateMessageworkload.Add this agentic workflows to your repo
To install this agentic workflow, run