Repository navigation
Roll the per-thread stats into one collapsed breakdown per section - #4587
Merged
Merged
Conversation
Every actual metric emitted one indented Thread N row per thread, inline, right under its own summary row: rows, rows read, executions, elapsed, CPU, logical reads, physical reads, scans, and read-ahead reads. A DOP 4 hash match already produced about thirty of those rows and DOP 8 produces hundreds, so the handful of summary numbers people actually open this panel for were buried in a scroll marathon. The summary rows are untouched. The per-thread numbers now live in one collapsed Per-thread breakdown sub-expander per affected section (Actual Statistics, Actual Timing, Actual I/O), with the threads grouped under a small header per metric. A metric with no per-thread data is skipped, so a section only grows a breakdown when there is something in it. Rows and executions list idle threads too, since a thread sitting at zero while its siblings work is exactly what someone opens the breakdown to see. The breakdown header carries a skew suffix when there is skew: the share test mirrors PlanAnalyzer's Rule 8 (Parallel Skew) exactly (same coordinator filter, same 1,000-rows-per-worker floor, same 0.80/0.50 share threshold) so the header never contradicts the warning the same plan raises, plus an idle-thread test Rule 8 does not make, since a thread that returned nothing at all while its siblings did real work reads as skew on sight even when the busiest thread is under Rule 8's share threshold. The shaping (which metrics have data, in what order, and the skew math) lives in a new pure ThreadBreakdown helper in PerformanceMonitor.PlanAnalysis so it can be pinned without WPF. The shared PlanViewerControl code-behind only renders what that helper returns, so Lite and the Darling Viewer both get the collapsed breakdown from this one change.
erikdarlingdata
marked this pull request as ready for review
September 28, 2026 15:42
This was referenced Sep 28, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4575. Part of #4511.
Why
The plan viewer's Properties panel emitted one indented "Thread N" row per
thread, inline, right under each summary row: rows, rows read, executions,
elapsed, CPU, logical reads, physical reads, scans, and read-ahead reads. A
DOP 4 hash match already produces about thirty of those rows, and DOP 8
produces hundreds, so the handful of summary numbers most people open this
panel for were buried in a scroll marathon.
Ports
erikdarlingdata/PerformanceStudio@ff7f1d9d59851b2f7438bdade83d710e4528a411(and its current
devform, which is unchanged since that commit for thiscode).
What changes
sub-expander per affected section (Actual Statistics, Actual Timing,
Actual I/O), with threads grouped under a small header per metric.
breakdown when there is something in it.
its siblings work is exactly what someone opens the breakdown to see);
Rows Read, Elapsed, CPU, Logical/Physical Reads, Scans, and Read-Ahead
Reads only list threads with a nonzero value.
test mirrors
PlanAnalyzer's Rule 8 (Parallel Skew) exactly — samecoordinator-thread filter, same 1,000-rows-per-worker floor, same 0.80 (≤2
workers) / 0.50 (>2 workers) share threshold — so the header never
contradicts the Parallel Skew warning the same plan raises. There is also
an idle-thread check Rule 8 does not make: a thread that returned nothing
at all while its siblings did real work reads as skew on sight even when
the busiest thread is under Rule 8's own share threshold.
moves into a new pure
ThreadBreakdownstatic class inPerformanceMonitor.PlanAnalysis. The sharedPlanViewerControlcode-behind's new
AddPerThreadBreakdownonly renders what that helperreturns; the previous inline nine-repeat block is gone.
OrangeBrush(0xFFB347) already used elsewhere in thissame panel for warnings, rather than adding a new brush.
Lite: gets this too (shared control) —
PlanViewerControllives inPerformanceMonitor.Ui, and both the Darling Viewer and Lite reference thatproject, so this one change reaches both hosts.
Not ported: none. This PR is the whole ff7f1d9 diff for this file.
Test plan
New file:
Darling/Darling.Tests/ViewerThreadBreakdownTests.cs, 12 pins onThreadBreakdown— no-more-than-one-thread returns null, an all-zero metricreturns null, idle threads included/excluded per metric flag, header text
and thread count, the skew suffix format, the 0.50 threshold at 3+ workers
(just over and just under), the 0.80 threshold at 2 workers (60/40 vs
81/19), the idle-thread-always-reads-as-skew case, the 1,000-rows-per-worker
floor, metric ordering/skip, and unit carry-through.
RED on
dev(pre-fix130bc6a9e): compile-only, sinceThreadBreakdowndoesn't exist there yet — 13
CS0103errors, confirmed in a detachedworktree at that sha with the new test file copied in.
Runtime mutation: changed the >2-worker share threshold from
0.50to0.90inThreadBreakdown.cs.Build_carries_a_skew_suffix_at_three_or_more_workers_over_the_fifty_percent_sharewent RED (
[FAIL]); reverted, rebuilt, GREEN again.Ran on this rig (in-process,
Darling.Tests.dllwithMicrosoft.WindowsDesktop.Appstripped from itsruntimeconfig.json):Total: 238, Errors: 0, Failed: 0, Skipped: 2, Not Run: 0Also ran every
Viewer*class discovered inDarling.Tests(160 classes,1,655 tests): 86 failures, all pre-existing and all
FileNotFoundException: PresentationFramework— WPF types this Mac can't load, unrelated to thischange (the same classes fail the same way with this branch's changes
reverted). No
PlanSync*classes exist in this tree.Builds (Release,
-p:EnableWindowsTargeting=true), each 0 warnings / 0errors:
PerformanceMonitor.PlanAnalysis,PerformanceMonitor.Ui,Darling.Tests,Lite/PerformanceMonitorLite.csproj,Lite.Tests,Darling/PerformanceMonitor.Darling.Viewer.Screenshot plan (for the Darling Viewer and Lite — same control):
(rows in the thousands per worker, so the skew math has something to
grade). Open the node's Properties panel.
rows exactly as before, no inline "Thread N" rows under any of them.
sub-expander, collapsed by default, right where the old inline rows used
to start.
Read, Executions for Actual Statistics; Elapsed, CPU for Actual Timing;
Logical Reads, Physical Reads, Scans, Read-Ahead Reads for Actual I/O),
each with its own indented "Thread N: value" list underneath.
Statistics shows an amber "(skewed: N max / M min)" suffix next to the
thread count, in the same amber this panel already uses for warnings.
If balanced, no suffix.
all-in-memory plan) doesn't appear in the breakdown at all.
CHANGELOG
SECTION: Changed
ENTRY: - The plan viewer rolls per-thread stats into one collapsed breakdown ([#4587]) - For an actual parallel plan, the per-thread stats in each properties section collapse into one "Per-thread breakdown (N threads)" section, as in PerformanceStudio, with a skew note in its header when the work is unbalanced, instead of an inline row per thread per metric.
REF: [#4587]: #4587