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
CloneForDataDrivenIteration is called once per data-driven test row (for both DataRow-folded and ITestDataSource-folded paths). It was allocating two Dictionary objects per iteration:
An intermediate snapshot Dictionary copied from _properties
The constructor's own copy of the incoming dictionary (via [with(properties)])
The second copy is unavoidable — the clone's property bag must be independent. The first copy is redundant.
Approach
The constructor's null/null branch (testMethod: null, testClassFullName: null) already does _properties = [with(properties)], which is a full shallow copy. Passing _properties directly instead of a pre-made snapshot eliminates the intermediate allocation while preserving identical observable behavior: the clone's property bag is fully isolated from the original.
Performance Evidence
Before: 2 Dictionary allocations per CloneForDataDrivenIteration call:
new Dictionary<string, object?>(_properties) — the snapshot
[with(properties)] inside the constructor — the actual clone
After: 1 Dictionary allocation per call:
[with(properties)] inside the constructor — a direct copy of _properties
Estimated saving: 1 Dictionary (≈ 104 B on .NET 9 for a small property bag) + O(n) copy per data-driven test iteration. For a test with 100 data rows, this removes 100 Dictionary allocations per test run.
Trade-offs
None. The change is purely mechanical — same API, same isolation guarantees, same outcome.
Reproducibility
To verify the allocation reduction, run a data-driven test suite with a profiler (e.g. PerfView / dotnet-trace) and observe the Dictionary..ctor callcount on CloneForDataDrivenIteration's stack.
Test Status
All existing CloneForDataDrivenIteration* unit tests cover: shallow copy semantics, isolation from original, fresh output state, TestRunCount propagation. CI will verify build and test results.
🤖 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 Perf Improver workflow. · 218.5 AIC · ⌖ 19.8 AIC · ⊞ 12.5K · [◷]( · ◷)
Add this agentic workflows to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/perf-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 perf-assist/skip-clone-dict-alloc-51b1bb7575f3e82b.
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 (56 of 56 lines)
From b704b9f6744c18e87aa68a756a517db88be233d2 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Sat, 4 Jul 2026 14:08:29 +0000
Subject: [PATCH] perf: skip intermediate Dictionary alloc in
CloneForDataDrivenIteration
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The constructor's null/null branch already copies the incoming dictionary
via a collection-expression ([with(properties)]). The previous code first
created a snapshot Dictionary and then passed it to the ctor, which copied
it again — two Dictionary allocations per data-driven test iteration.
Pass _properties directly to the ctor so it copies once, saving one
Dictionary + O(n) copy per data-driven test iteration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../Services/TestContextImplementation.cs | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs
index 5efc98a..f2d0a4e 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs+++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs@@ -524,16 +524,15 @@ private SynchronizedStringBuilder GetTestContextMessagesStringBuilder()
/// <returns>A fresh context suitable for one folded data-driven iteration.</returns>
internal TestContextImplementation CloneForDataDrivenIteration()
{
- // Take a shallow snapshot of the current property bag so that the clone starts with- // the same properties (including TestNameLabel / FullyQualifiedTestClassNameLabel and- // anything merged from AssemblyInitialize / ClassInitialize) but is otherwise isolated.- // Per-iteration mutations to the clone's property bag won't leak back to this ins
... (truncated)
Goal and Rationale
CloneForDataDrivenIterationis called once per data-driven test row (for bothDataRow-folded andITestDataSource-folded paths). It was allocating two Dictionary objects per iteration:snapshotDictionary copied from_properties[with(properties)])The second copy is unavoidable — the clone's property bag must be independent. The first copy is redundant.
Approach
The constructor's
null/nullbranch (testMethod: null, testClassFullName: null) already does_properties = [with(properties)], which is a full shallow copy. Passing_propertiesdirectly instead of a pre-made snapshot eliminates the intermediate allocation while preserving identical observable behavior: the clone's property bag is fully isolated from the original.Performance Evidence
Before: 2 Dictionary allocations per
CloneForDataDrivenIterationcall:new Dictionary<string, object?>(_properties)— the snapshot[with(properties)]inside the constructor — the actual cloneAfter: 1 Dictionary allocation per call:
[with(properties)]inside the constructor — a direct copy of_propertiesEstimated saving: 1 Dictionary (≈ 104 B on .NET 9 for a small property bag) + O(n) copy per data-driven test iteration. For a test with 100 data rows, this removes 100 Dictionary allocations per test run.
Trade-offs
None. The change is purely mechanical — same API, same isolation guarantees, same outcome.
Reproducibility
To verify the allocation reduction, run a data-driven test suite with a profiler (e.g. PerfView / dotnet-trace) and observe the
Dictionary..ctorcallcount onCloneForDataDrivenIteration's stack.Test Status
All existing
CloneForDataDrivenIteration*unit tests cover: shallow copy semantics, isolation from original, fresh output state, TestRunCount propagation. CI will verify build and test results.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
perf-assist/skip-clone-dict-alloc-51b1bb7575f3e82b.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 (56 of 56 lines)