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.
AttributeExtensions.IsIgnored() is called twice per test execution (class-level + method-level checks in UnitTestRunner.cs). The original implementation always allocated:
A compiler-generated yield-iterator state machine via GetAttributes<ConditionBaseAttribute>() (~48 bytes per call)
A GroupBy Lookup via LINQ GroupBy(), even when the source sequence is empty
For the overwhelming majority of tests — those with no [Ignore] or other ConditionBaseAttribute — both allocations were wasted on every test execution.
Focus Area
Code-Level Efficiency — eliminating unnecessary heap allocations in the test execution hot path.
Approach
Replace GetAttributes() + GroupBy() with a direct walk of GetCustomAttributesCached(), following the same pattern established by PropertyBag.OfType(), GetRetryAttribute(), and TryExecuteFoldedDataDrivenTestsAsync() in this codebase:
Case
Before
After
No condition attrs (most common)
yield state machine + GroupBy Lookup
0 allocations — returns immediately
Single condition attr
yield state machine + GroupBy Lookup
0 extra allocations — evaluates directly
Multiple condition attrs
yield state machine + GroupBy Lookup
List(T) + GroupBy Lookup (same as before, minus state machine)
Energy Efficiency Evidence
Proxy metric: heap allocation count per test run → GC pressure → CPU time in GC → energy consumption
For a 10,000-test suite where no tests have condition attributes (typical):
Calls to IsIgnored(): 20,000 (2 per test × 10,000 tests)
GroupBy Lookup objects eliminated: ~20,000 (one per call on empty sequence)
Net GC pressure reduction: ~40,000 objects / ~1 MB per 10,000-test run
Methodology: Allocation sizes from .NET runtime + IL analysis of yield-generated state machines. The GroupBy Lookup is created on first MoveNext() call even on empty sources.
🌱 Green Software Foundation Context
Hardware Efficiency — fewer managed allocations reduce DRAM refresh cycles and GC CPU overhead per unit of test work. The GC cost is proportional to allocation rate; eliminating ~40,000 objects per 10,000-test run directly reduces CPU cycles spent collecting dead objects.
Trade-offs
The multi-attribute path uses GroupBy on a List<> (same semantics — minus the yield state machine overhead). No semantic change.
Code is longer (~90 lines vs ~30 lines) but follows the same pattern established elsewhere in this codebase.
Reproducibility
# Measure heap allocations with dotnet-trace:
dotnet-trace collect --providers Microsoft-DotNETRuntime:GCAllocationTick -- dotnet test
Test Status
✅ Build: 0 warnings, 0 errors (net9.0).
Local unit test execution is blocked in this CI environment by a package feed issue (AwesomeAssertions.dll path resolution error for MSTestAdapter.PlatformServices.UnitTests). The change is a pure refactoring — semantically equivalent to the original for all three cases. CI will run the full suite.
The bundle 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 25752925292 -n agent -D /tmp/agent-25752925292
# Fetch the bundle into a local branch
git fetch /tmp/agent-25752925292/aw-efficiency-optimize-isignored-allocs-1778609590.bundle refs/heads/efficiency/optimize-isignored-allocs-1778609590:refs/heads/efficiency/optimize-isignored-allocs-1778609590-612a40a2cdae11a1
git checkout efficiency/optimize-isignored-allocs-1778609590-612a40a2cdae11a1
# Push the branch to origin
git push origin efficiency/optimize-isignored-allocs-1778609590-612a40a2cdae11a1
# Create the pull request
gh pr create --title '[Efficiency Improver] perf: eliminate yield-iterator and GroupBy allocations in IsIgnored() hot path' --base main --head efficiency/optimize-isignored-allocs-1778609590-612a40a2cdae11a1 --repo microsoft/testfx
🤖 This PR was created by Daily Efficiency Improver, an automated AI assistant focused on reducing the energy consumption and computational footprint of this repository.
Closes #8143
Goal and Rationale
AttributeExtensions.IsIgnored()is called twice per test execution (class-level + method-level checks inUnitTestRunner.cs). The original implementation always allocated:GetAttributes<ConditionBaseAttribute>()(~48 bytes per call)GroupBy(), even when the source sequence is emptyFor the overwhelming majority of tests — those with no
[Ignore]or otherConditionBaseAttribute— both allocations were wasted on every test execution.Focus Area
Code-Level Efficiency — eliminating unnecessary heap allocations in the test execution hot path.
Approach
Replace
GetAttributes()+GroupBy()with a direct walk ofGetCustomAttributesCached(), following the same pattern established byPropertyBag.OfType(),GetRetryAttribute(), andTryExecuteFoldedDataDrivenTestsAsync()in this codebase:Energy Efficiency Evidence
Proxy metric: heap allocation count per test run → GC pressure → CPU time in GC → energy consumption
For a 10,000-test suite where no tests have condition attributes (typical):
IsIgnored(): 20,000 (2 per test × 10,000 tests)🌱 Green Software Foundation Context
Hardware Efficiency — fewer managed allocations reduce DRAM refresh cycles and GC CPU overhead per unit of test work. The GC cost is proportional to allocation rate; eliminating ~40,000 objects per 10,000-test run directly reduces CPU cycles spent collecting dead objects.
Trade-offs
GroupByon aList<>(same semantics — minus the yield state machine overhead). No semantic change.Reproducibility
Test Status
✅ Build: 0 warnings, 0 errors (net9.0).
Note
This was originally intended as a pull request, but the git push operation failed.
Workflow Run: View run details and download bundle artifact
The bundle file is available in the
agentartifact in the workflow run linked above.To create a pull request with the changes: