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
For data-driven tests using ITestDataSource (e.g. [DynamicData]), the inner execution loop calls ExecuteTestWithDataSourceAsync once per data row. Each call previously created a fresh ReflectionTestMethodInfo wrapper, and each ComputeDefaultDisplayName call on that wrapper invoked _methodInfo.GetParameters() — which always allocates a new ParameterInfo[] array (CLR safety guarantee). This is the same pattern already fixed for TestMethodInfo.ParameterTypes and AssemblyEnumerator.TryUnfoldITestDataSource in PR #9514.
Focus Area
Code-Level Efficiency — unnecessary object creation per data row.
Approach
Two complementary changes:
ReflectionTestMethodInfo.GetParameters() — add a _cachedParameters field; subsequent calls return the cached array instead of delegating to _methodInfo.GetParameters() every time.
TestMethodRunner — add a _cachedReflectionMethodInfo lazy field so the wrapper object is created once per TestMethodRunner instance and reused across all data rows. Both _testMethodInfo.MethodInfo and _test.DisplayName are immutable for the lifetime of a TestMethodRunner, so sharing a single wrapper is safe.
Energy Efficiency Evidence
Proxy metric: heap allocations (fewer allocations = less GC pressure = less CPU energy for garbage collection; also reduces DRAM refresh pressure).
Scenario
Before
After
[DynamicData] test with N=1 row
1 ReflectionTestMethodInfo + 1 ParameterInfo[]
1 + 1 (same)
[DynamicData] test with N=100 rows
100 + 100 = 200 allocations
1 + 1 = 2 allocations
[DynamicData] test with N=1000 rows
1000 + 1000 = 2000 allocations
1 + 1 = 2 allocations
Savings scale linearly with row count. Large data-driven test suites (common in parameterised unit tests and table-driven integration tests) benefit most.
Green Software Foundation context:
Hardware Efficiency: Reduces short-lived object churn on the managed heap, improving GC throughput and reducing CPU cycles spent in collection.
Software Carbon Intensity (SCI): Fewer allocations per functional unit (one test execution) → lower energy per test run → lower SCI.
Trade-offs
Correctness: ReflectionTestMethodInfo wraps an immutable MethodInfo; GetParameters() always returns the same logical data. Caching does not change observable behaviour.
Complexity: Adds one ParameterInfo[]? field (8 bytes on 64-bit) to ReflectionTestMethodInfo and one ReflectionTestMethodInfo? field to TestMethodRunner. Trivial cost.
MakeGenericMethod: MakeGenericMethod creates a new ReflectionTestMethodInfo with a different underlying MethodInfo, so it correctly starts with a null _cachedParameters. ✅
For allocation benchmarks: use dotnet-trace or BenchmarkDotNet with [DynamicData] test over 100+ rows and compare Gen0/Gen1 GC counts.
Test Status
Both changed source projects (TestFramework.csproj and MSTestAdapter.PlatformServices.csproj) build cleanly with Build succeeded on the pinned SDK. Full CI will validate across all target frameworks.
🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Efficiency Improver workflow. · 396.1 AIC · ⌖ 21 AIC · ⊞ 13.6K · [◷]( · ◷)
Add this agentic workflows to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/efficiency-improver.md@main
Note
This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch efficiency/cache-reflection-method-info-across-data-rows-f161f0f322668db8.
To fix the permissions issue, go to Settings → Actions → General and enable Allow GitHub Actions to create and approve pull requests. See also: gh-aw FAQ
Show patch preview (99 of 99 lines)
From 351ed720493fd346ec38756bc37caefa3353e1c8 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 3 Jul 2026 22:13:56 +0000
Subject: [PATCH] perf: cache ReflectionTestMethodInfo and GetParameters()
across data rows
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
For data-driven tests using ITestDataSource (e.g. [DynamicData]), the
execution loop calls ExecuteTestWithDataSourceAsync once per data row.
Each call previously created a fresh ReflectionTestMethodInfo wrapper,
and each ComputeDefaultDisplayName call on that wrapper called
_methodInfo.GetParameters() which always allocates a new ParameterInfo[]
(CLR safety guarantee — same pattern fixed in TestMethodInfo.ParameterTypes
by PR #9514).
Changes:
- ReflectionTestMethodInfo.GetParameters(): add _cachedParameters field,
return cached result on subsequent calls via ??= operator.
- TestMethodRunner: add _cachedReflectionMethodInfo lazy field; reuse the
same wrapper instance across all data rows instead of allocating per row.
Both _testMethodInfo.MethodInfo and _test.DisplayName are immutable for
the lifetime of TestMethodRunner, so sharing is safe.
Impact (proxy metric: heap allocations):
Before: N ReflectionTestMethodInfo + N ParameterInfo[] per data-driven test
After: 1 ReflectionTestMethodInfo + 1 ParameterInfo[] per data-driven test
For 100 rows: ~198 fewer heap allocations per test method execution
This continues the work started in PR #9514 (GetParameters caching in
AssemblyEnumerator and TestMethodInfo.ParameterTypes).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../Execution/TestMethodRunner.cs | 15 ++++++++++++---
.../Internal/ReflectionTestMethodInfo.cs | 3 ++-
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestMethodRunner.cs b/src/Adapter/MSTestAdapter.Platfo
... (truncated)
Goal and Rationale
For data-driven tests using
ITestDataSource(e.g.[DynamicData]), the inner execution loop callsExecuteTestWithDataSourceAsynconce per data row. Each call previously created a freshReflectionTestMethodInfowrapper, and eachComputeDefaultDisplayNamecall on that wrapper invoked_methodInfo.GetParameters()— which always allocates a newParameterInfo[]array (CLR safety guarantee). This is the same pattern already fixed forTestMethodInfo.ParameterTypesandAssemblyEnumerator.TryUnfoldITestDataSourcein PR #9514.Focus Area
Code-Level Efficiency — unnecessary object creation per data row.
Approach
Two complementary changes:
ReflectionTestMethodInfo.GetParameters()— add a_cachedParametersfield; subsequent calls return the cached array instead of delegating to_methodInfo.GetParameters()every time.TestMethodRunner— add a_cachedReflectionMethodInfolazy field so the wrapper object is created once perTestMethodRunnerinstance and reused across all data rows. Both_testMethodInfo.MethodInfoand_test.DisplayNameare immutable for the lifetime of aTestMethodRunner, so sharing a single wrapper is safe.Energy Efficiency Evidence
Proxy metric: heap allocations (fewer allocations = less GC pressure = less CPU energy for garbage collection; also reduces DRAM refresh pressure).
[DynamicData]test with N=1 rowReflectionTestMethodInfo+ 1ParameterInfo[][DynamicData]test with N=100 rows[DynamicData]test with N=1000 rowsSavings scale linearly with row count. Large data-driven test suites (common in parameterised unit tests and table-driven integration tests) benefit most.
Green Software Foundation context:
Trade-offs
ReflectionTestMethodInfowraps an immutableMethodInfo;GetParameters()always returns the same logical data. Caching does not change observable behaviour.ParameterInfo[]?field (8 bytes on 64-bit) toReflectionTestMethodInfoand oneReflectionTestMethodInfo?field toTestMethodRunner. Trivial cost.MakeGenericMethodcreates a newReflectionTestMethodInfowith a different underlyingMethodInfo, so it correctly starts with a null_cachedParameters. ✅Reproducibility
For allocation benchmarks: use dotnet-trace or BenchmarkDotNet with
[DynamicData]test over 100+ rows and compareGen0/Gen1GC counts.Test Status
Both changed source projects (
TestFramework.csprojandMSTestAdapter.PlatformServices.csproj) build cleanly withBuild succeededon the pinned SDK. Full CI will validate across all target frameworks.Add this agentic workflows to your repo
To install this agentic workflow, run
Note
This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch
efficiency/cache-reflection-method-info-across-data-rows-f161f0f322668db8.Click here to create the pull request
To fix the permissions issue, go to Settings → Actions → General and enable Allow GitHub Actions to create and approve pull requests. See also: gh-aw FAQ
Show patch preview (99 of 99 lines)