Skip to content

Fix O(n²) per-node UID lookup in TestNodeUidListFilter re-run path#7806

Merged
Evangelink merged 2 commits intomainfrom
copilot/efficiency-improver-2026-04
Apr 24, 2026
Merged

Fix O(n²) per-node UID lookup in TestNodeUidListFilter re-run path#7806
Evangelink merged 2 commits intomainfrom
copilot/efficiency-improver-2026-04

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 24, 2026

BFSTestNodeVisitor was performing an O(n) Any() linear scan over TestNodeUidListFilter.TestNodeUids for every test node visited, resulting in O(n×m) total work (n = test nodes, m = filter UIDs). This is hit on every retry/re-run via RetryExecutionFilterFactory.

Changes

  • BFSTestNodeVisitor.VisitAsync: Precompute a HashSet<PlatformTestNodeUid> from TestNodeUidListFilter.TestNodeUids once before the BFS loop; replace Any() with HashSet.Contains() for O(1) per-node lookup. Adds a using alias to disambiguate from the framework-internal TestNodeUid type.
  • TestingPlatformExplorer/TestingFramework.cs (sample): Same fix — extract the HashSet outside the foreach and use Contains instead of Any.
// Before: O(m) scan per node, O(n×m) total
|| listFilter.TestNodeUids.Any(uid => currentNode.StableUid.ToPlatformTestNodeUid() == uid)

// After: O(1) per node, O(n+m) total
HashSet<PlatformTestNodeUid>? uidFilterSet = _testExecutionFilter is TestNodeUidListFilter listFilter
    ? [.. listFilter.TestNodeUids]
    : null;
// ...
|| uidFilterSet.Contains(currentNode.StableUid.ToPlatformTestNodeUid())

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 4m6vsblobprodcus384.vsblob.vsassets.io
    • Triggering command: .dotnet/dotnet .dotnet/dotnet build samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.csproj (dns block)
  • 7k6vsblobprodcus337.vsblob.vsassets.io
    • Triggering command: .dotnet/dotnet .dotnet/dotnet build samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.csproj (dns block)
  • jrqvsblobprodcus343.vsblob.vsassets.io
    • Triggering command: .dotnet/dotnet .dotnet/dotnet build src/Adapter/MSTest.Engine/MSTest.Engine.csproj --no-restore (dns block)
  • pc2vsblobprodcus360.vsblob.vsassets.io
    • Triggering command: .dotnet/dotnet .dotnet/dotnet build samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingPlatformExplorer.csproj (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested review from Copilot and removed request for Copilot April 24, 2026 09:10
Copilot AI linked an issue Apr 24, 2026 that may be closed by this pull request
1 task
…1) membership test

Agent-Logs-Url: https://github.com/microsoft/testfx/sessions/fa2343e0-6020-4160-8937-17fa45259f87

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 24, 2026 09:22
Copilot AI changed the title [WIP] Review PR microsoft/testfx#7787 for efficiency improvements Fix O(n²) per-node UID lookup in TestNodeUidListFilter re-run path Apr 24, 2026
Copilot AI requested a review from Evangelink April 24, 2026 09:23
@Evangelink Evangelink marked this pull request as ready for review April 24, 2026 11:35
Copilot AI review requested due to automatic review settings April 24, 2026 11:35
@Evangelink Evangelink enabled auto-merge (squash) April 24, 2026 11:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Optimizes test-node UID filtering during traversal/execution to avoid repeated linear scans when re-running/retrying tests via TestNodeUidListFilter, reducing total work from O(n×m) to ~O(n+m).

Changes:

  • Precomputes a HashSet of filtered UIDs once and uses Contains for O(1) per-node membership checks in BFSTestNodeVisitor.VisitAsync.
  • Applies the same pattern in the TestingPlatformExplorer sample execution loop.
Show a summary per file
File Description
src/Adapter/MSTest.Engine/Engine/BFSTestNodeVisitor.cs Builds a UID HashSet once and replaces per-node Any() scans with Contains().
samples/public/TestingPlatformExamples/TestingPlatformExplorer/TestingFramework/TestingFramework.cs Uses a precomputed UID HashSet for faster filtering in the sample runner loop.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Comment on lines +91 to +92
if (uidFilterSet is null
|| uidFilterSet.Contains(currentNode.StableUid.ToPlatformTestNodeUid()))
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentNode.StableUid is already Microsoft.Testing.Platform.Extensions.Messages.TestNodeUid, so calling ToPlatformTestNodeUid() here allocates a new TestNodeUid per visited node. If you want this to be allocation-free (and likely drop the PlatformTestNodeUid alias), you can check uidFilterSet.Contains(currentNode.StableUid) directly.

Copilot uses AI. Check for mistakes.
Comment on lines +151 to 153
if (uidFilterSet is not null
&& !uidFilterSet.Contains($"{test.DeclaringType!.FullName}.{test.Name}"))
{
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this sample, uidFilterSet is a HashSet<TestNodeUid> but Contains is called with an interpolated string, which relies on the implicit string -> TestNodeUid conversion and allocates a new TestNodeUid per check. If you want this to be allocation-free, consider storing uid.Value in a HashSet<string> (e.g., with StringComparer.Ordinal) and comparing against the computed UID string.

Copilot uses AI. Check for mistakes.
@Evangelink Evangelink merged commit dc9dae5 into main Apr 24, 2026
43 checks passed
@Evangelink Evangelink deleted the copilot/efficiency-improver-2026-04 branch April 24, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Efficiency Improver] Monthly Activity 2026-04

4 participants