Skip to content

[perf-improver] perf: skip intermediate Dictionary alloc in CloneForDataDrivenIteration #9602

Description

@github-actions

Goal and Rationale

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:

  1. An intermediate snapshot Dictionary copied from _properties
  2. 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.

Click here to create the pull request

To fix the permissions issue, go to SettingsActionsGeneral 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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/agentic-workflowsGitHub agentic workflow definitions under .github/workflows/*.md.area/performanceRuntime / build performance / efficiency.type/automationCreated or maintained by an agentic workflow.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions