You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
🤖 This PR was created by Daily Efficiency Improver, an automated AI assistant focused on reducing the energy consumption and computational footprint of this repository.
Goal and Rationale
Eliminate heap allocations that occur on every test execution in the MSTest test runner hot path. These allocations scale linearly with test count and contribute to GC pressure, which wastes CPU cycles (and thus energy) on collection work.
Focus area: Code-Level Efficiency
Changes
1. TestMethodInfo.GetRetryAttribute() — 1 allocation per TestMethodInfo construction eliminated
GetAttributes<T>() is a yield return iterator — calling it allocates a compiler-generated state machine object on the heap. For 10,000 tests, that's 10,000 avoidable allocations at construction time.
Direct iteration of the already-cached Attribute[] — zero extra allocation. Preserves the duplicate-attribute detection and the custom TypeInspectionException.
2. TestMethodRunner.TryExecuteDataSourceBasedTestsAsync() — 2 allocations per test execution eliminated (common case)
GetAttributes<DataSourceAttribute>() allocates a yield iterator state machine and a DataSourceAttribute[] array via [.. iterator]. For most tests (no data source), these are 2 wasted allocations per test execution.
IsAttributeDefined<T>() iterates the cached array in-place — zero allocation. Since DataSourceAttribute is sealed and has AllowMultiple = false, presence equals exactly one instance.
Energy Efficiency Evidence
Proxy metric: Heap allocation count (measures GC pressure, which translates directly to CPU energy use for garbage collection)
Location
Before
After
Savings
GetRetryAttribute()
1 state machine alloc per test construction
0
1 alloc eliminated
TryExecuteDataSourceBasedTestsAsync()
2 allocs per test execution (non-DataSource)
0
2 allocs eliminated
At scale (10,000 tests): ~30,000 heap allocations eliminated per test run (10k constructions + 20k executions), reducing GC trigger frequency and associated CPU overhead.
Methodology: Static code analysis of allocation sites. Validated by reading the IL model: yield return generates a compiler-synthesized class that is heap-allocated on each GetEnumerator() call; [.. IEnumerable<T>] additionally allocates a new array.
Green Software Foundation
Hardware Efficiency: Fewer allocations = less DRAM bandwidth and GC CPU per joule
Energy Proportionality: GC cost scales with allocation rate; reducing it improves the energy/work ratio as test suite size grows
Trade-offs
Slightly more verbose code in GetRetryAttribute() but equivalent logic; the comment explains why.
No behaviour change: same result, same error paths, same exceptions.
The patch file is available in the agent artifact in the workflow run linked above.
To create a pull request with the changes:
# Download the artifact from the workflow run
gh run download 25650636054 -n agent -D /tmp/agent-25650636054
# Create a new branch
git checkout -b efficiency/avoid-yield-iterator-get-retry-attribute-be0151402d39b1b6
# Apply the patch (--3way handles cross-repo patches where files may already exist)
git am --3way /tmp/agent-25650636054/aw-efficiency-avoid-yield-iterator-get-retry-attribute.patch
# Push the branch to origin
git push origin efficiency/avoid-yield-iterator-get-retry-attribute-be0151402d39b1b6
# Create the pull request
gh pr create --title '[Efficiency Improver] perf: eliminate yield-iterator allocations in test execution hot path' --base main --head efficiency/avoid-yield-iterator-get-retry-attribute-be0151402d39b1b6 --repo microsoft/testfx
Show patch preview (101 of 101 lines)
From 5ee2f140680aa644dd745fca452df3109bc1ab78 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 11 May 2026 04:50:33 +0000
Subject: [PATCH] perf: eliminate yield-iterator allocations in test execution
hot path
GetRetryAttribute() called GetAttributes<RetryBaseAttribute>() which allocates
a compiler-generated state machine (IEnumerator<T> via yield return) on every
test method construction. Replace with direct iteration of the cached Attribute[]
from GetCustomAttributesCached(). Preserves duplicate-attribute detection and
the custom TypeInspectionException.
TryExecuteDataSourceBasedTestsAsync() called _testMethodInfo.GetAttributes<DataSourceAttribute>()
which allocates a yield iterator state machine AND a DataSourceAttribute[] array.
Replace with ReflectHelper.Instance.IsAttributeDefined<DataSourceAttribute>(),
which iterates the same cached array without any allocation. Since DataSourceAttribute
is sealed and does not allow multiple, presence == exactly one instance.
Proxy metric: heap allocation count
- GetRetryAttribute: 1 allocation eliminated per TestMethodInfo construction- TryExecuteDataSourceBasedTestsAsync: 2 allocations eliminated per test execution
(for tests without a DataSourceAttribute -- the common case)
- Estimated: ~480 KB GC pressure reduction per 10,000-test run
GSF principle: Hardware Efficiency -- fewer allocations = less DRAM and GC CPU
per joule; Energy Proportionality -- GC cost scales with allocation rate.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../Execution/TestMethodInfo.cs | 24 ++++++++++---------
.../Execution/TestMethodRunner.cs | 11 +++++----
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestMethodInfo.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestMethodInfo.cs
index e45ed74e5..af4aef0eb 100644
--- a/src/Adapter/MSTestA
... (truncated)
🤖 This PR was created by Daily Efficiency Improver, an automated AI assistant focused on reducing the energy consumption and computational footprint of this repository.
Goal and Rationale
Eliminate heap allocations that occur on every test execution in the MSTest test runner hot path. These allocations scale linearly with test count and contribute to GC pressure, which wastes CPU cycles (and thus energy) on collection work.
Focus area: Code-Level Efficiency
Changes
1.
TestMethodInfo.GetRetryAttribute()— 1 allocation per TestMethodInfo construction eliminatedBefore:
GetAttributes<T>()is ayield returniterator — calling it allocates a compiler-generated state machine object on the heap. For 10,000 tests, that's 10,000 avoidable allocations at construction time.After:
Direct iteration of the already-cached
Attribute[]— zero extra allocation. Preserves the duplicate-attribute detection and the customTypeInspectionException.2.
TestMethodRunner.TryExecuteDataSourceBasedTestsAsync()— 2 allocations per test execution eliminated (common case)Before:
GetAttributes<DataSourceAttribute>()allocates a yield iterator state machine and aDataSourceAttribute[]array via[.. iterator]. For most tests (no data source), these are 2 wasted allocations per test execution.After:
IsAttributeDefined<T>()iterates the cached array in-place — zero allocation. SinceDataSourceAttributeissealedand hasAllowMultiple = false, presence equals exactly one instance.Energy Efficiency Evidence
Proxy metric: Heap allocation count (measures GC pressure, which translates directly to CPU energy use for garbage collection)
GetRetryAttribute()TryExecuteDataSourceBasedTestsAsync()At scale (10,000 tests): ~30,000 heap allocations eliminated per test run (10k constructions + 20k executions), reducing GC trigger frequency and associated CPU overhead.
Methodology: Static code analysis of allocation sites. Validated by reading the IL model:
yield returngenerates a compiler-synthesized class that is heap-allocated on eachGetEnumerator()call;[.. IEnumerable<T>]additionally allocates a new array.Green Software Foundation
Trade-offs
GetRetryAttribute()but equivalent logic; the comment explains why.Reproducibility
Test Status
dotnet build MSTestAdapter.PlatformServices.csproj -p:SignAssembly=false— 0 warnings, 0 errors (net8.0, net9.0)dotnet build MSTest.TestAdapter.csproj -p:SignAssembly=false— 0 warnings, 0 errors (net8.0, net9.0)Note
This was originally intended as a pull request, but the git push operation failed.
Workflow Run: View run details and download patch artifact
The patch file is available in the
agentartifact in the workflow run linked above.To create a pull request with the changes:
Show patch preview (101 of 101 lines)