[efficiency-improver] Single-pass min/max for Azure DevOps result date aggregation

Open
#11,396 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
25/100
Issue type
Refactor
Clarity
Clearly specified
Activity status
Stale
Tech stack
csharp
Domain
testing-qa

Research direction

Review AzureDevOpsResultIdStore.cs and AzureDevOpsResultIdStore.AttemptHistory.cs, starting with GetEarliestStartedDate and GetLatestCompletedDate. Run the AzureDevOps-filtered Microsoft.Testing.Extensions.UnitTests suite and build.sh. Done means both method pairs use the intended single-pass aggregation without public API changes and the reported tests and formatting checks pass.

Written by the indexing model from the issue text.

Description

agentic-workflows area/performance type/automation

[!TIP]
Your pull request is ready to create! 🎉 ✅

Everything is OK—the changes have been pushed to branch efficiency/azuredevops-single-pass-min-max-1d374458b554a417. Please review the changes, including any protected files, before creating the pull request.

Create the pull request

The original pull request description is below.


Goal and rationale

AzureDevOpsResultIdStore.GetEarliestStartedDate/GetLatestCompletedDate (public AzureDevOpsTestCaseResult overload in AzureDevOpsResultIdStore.cs, plus the internal AzureDevOpsTestSubResult overload in AzureDevOpsResultIdStore.AttemptHistory.cs) used attempts.Where(a => a.X is not null).Min/Max(a => a.X). Each pair of calls is always invoked together from the AzureDevOps live-publishing result-flush hot path, so every invocation double-enumerated the attempts list and allocated a Where iterator per call, for no benefit over a single manual pass.

Focus area

Code-Level Efficiency — replacing a LINQ-based double-enumeration with a single-pass loop.

Approach

Replaced both Where().Min()/Where().Max() chains (4 methods total across the two files) with manual single-pass foreach loops tracking a running min/max, mirroring the existing hand-rolled SumDurations/AddDurations pattern already present in the same files. No public API signature changes.

Energy efficiency evidence

Proxy metrics: execution time (wall clock) and memory allocation (GC.GetAllocatedBytesForCurrentThread()), both mapping directly to reduced CPU cycles and reduced GC pressure — i.e. lower energy draw per call, consistent with the Green Software Foundation's Hardware Efficiency principle (doing the same work with less computation).

Standalone console benchmark (net8.0 Release, Stopwatch + GC.GetAllocatedBytesForCurrentThread(), 2,000,000 call-pairs of GetEarliestStartedDate+GetLatestCompletedDate over a representative 5-element attempts list):

Old (LINQ) New (single-pass loop) Delta
Time 581 ms 308 ms ~1.9x faster
Allocated 288,000,040 B 160,000,040 B ~44% less allocation

This is a hot path invoked once per result flush during AzureDevOps live test-result publishing, so the reduction compounds across large test runs.

Trade-offs

Slightly more verbose than the LINQ one-liners, but the same shape as the existing SumDurations helper in the same file, so it's consistent with the surrounding code style rather than introducing a new pattern. No readability concerns beyond that.

Reproducibility

Benchmark: standalone console app (net8.0, -c Release) exercising both implementations with the same synthetic attempts list, measuring Stopwatch.ElapsedMilliseconds and GC.GetAllocatedBytesForCurrentThread() deltas across 2,000,000 call-pairs (warmup of 1,000 iterations before each measured loop, GC.Collect() before each measurement).

Test status

  • ./build.sh: Build succeeded, 0 warnings, 0 errors.
  • Microsoft.Testing.Extensions.UnitTests (net8.0, full suite): 1839/1839 passed (37 pre-existing skips).
  • Microsoft.Testing.Extensions.UnitTests filtered to FullyQualifiedName~AzureDevOps: 387/387 passed.
  • dotnet format whitespace TestFx.slnx --verify-no-changes --include <both changed files>: clean, no formatting issues.

[!NOTE]
GitHub Actions is not permitted to create or approve pull requests in this repository.

The changes have been pushed to branch efficiency/azuredevops-single-pass-min-max-1d374458b554a417 and are ready to review.

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 (32 of 105 lines)
From 9e3ace0ad133c61d5b334fd981c366325772c36b Mon Sep 17 00:00:00 2001
X-GH-AW-Base-Commit: f0fe5a89980d2b29daeb3c949c08d7f61b9cf939
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Sun, 20 Sep 2026 21:51:26 +0000
Subject: [PATCH] Use single-pass loops for AzureDevOps result date aggregation

Replace Where().Min()/Where().Max() LINQ chains with manual single-pass
foreach loops tracking running min/max in AzureDevOpsResultIdStore's
GetEarliestStartedDate/GetLatestCompletedDate (both the public
AzureDevOpsTestCaseResult overload and the internal
AzureDevOpsTestSubResult overload). Mirrors the existing hand-rolled
SumDurations/AddDurations pattern already used in the same files.

Both method pairs are invoked together per-attempt from the AzureDevOps
live-publishing result-flush hot path, so each call previously
double-enumerated the attempts list and allocated a Where iterator.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 ...AzureDevOpsResultIdStore.AttemptHistory.cs | 26 +++++++++++++++++--
 .../AzureDevOpsResultIdStore.cs               | 26 +++++++++++++++++--
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs
index 3ae5eda..a03f7e7 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs
@@ -59,10 +59,32 @@ or AzureDevOpsLivePublishingConstants.NotExecutedTestOutcome
                 : right.Value > long.MaxValue - left.Value ? long.MaxValue : left.Value + right.Value;
 
     private static DateTimeOffset? GetEarliestStartedDate(IReadOnlyList<AzureDevOpsTestSubResult> attempts)
-        => attempts.Where(attempt => attemp
... (truncated)

[!WARNING]

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • southcentralus0.in.applicationinsights.azure.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "southcentralus0.in.applicationinsights.azure.com"

See Network Configuration for more information.

🤖 Automated content by GitHub Copilot. Generated by the Efficiency Improver workflow. · copilot · auto · 168.8 AIC · ⌖ 13.9 AIC · ⊞ 17.8K · [◷]( · )

Add this agentic workflow to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/efficiency-improver.md@main
Dominant language
C#
Stars
1k
Forks
312
Avg merge
7h 46m
Merged PRs (30d)
465

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/testfx

All issues in microsoft/testfx

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.