Skip to content

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

Merged
Evangelink merged 2 commits into
mainfrom
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 into
mainfrom
copilot/efficiency-improver-2026-04

Conversation

Copilot AI commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

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
@github-actions github-actions Bot added the type/ai-inspected Reviewed or generated with AI assistance. label Apr 24, 2026
@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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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()))

Copilot AI Apr 24, 2026

Copy link

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}"))
{

Copilot AI Apr 24, 2026

Copy link

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

Labels

type/ai-inspected Reviewed or generated with AI assistance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Efficiency Improver] Monthly Activity 2026-04

4 participants