From 4b67558011e74a02a02c00861fab769f32fee059 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:59:13 -0400 Subject: [PATCH] Format memory grants as KB/MB/GB instead of raw KB Add FormatMemoryGrantKB helper to TextFormatter that picks the most readable unit: KB under 1024, MB with 1 decimal up to 1 GB, GB with 2 decimals above. Applied in both the text output and the runtime summary pane. Closes #68 Co-Authored-By: Claude Opus 4.6 --- .../Controls/PlanViewerControl.axaml.cs | 3 ++- src/PlanViewer.Core/Output/TextFormatter.cs | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs b/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs index 8edde32..363d967 100644 --- a/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs +++ b/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs @@ -18,6 +18,7 @@ using PlanViewer.App.Helpers; using PlanViewer.App.Mcp; using PlanViewer.Core.Models; +using PlanViewer.Core.Output; using PlanViewer.Core.Services; using AvaloniaPath = Avalonia.Controls.Shapes.Path; @@ -2622,7 +2623,7 @@ static string EfficiencyColor(double pct) => pct >= 80 ? "#E4E6EB" ? (double)mg.MaxUsedMemoryKB / mg.GrantedMemoryKB * 100 : 100; var grantColor = EfficiencyColor(grantPct); AddRow("Memory grant", - $"{mg.GrantedMemoryKB:N0} KB granted, {mg.MaxUsedMemoryKB:N0} KB used ({grantPct:N0}%)", + $"{TextFormatter.FormatMemoryGrantKB(mg.GrantedMemoryKB)} granted, {TextFormatter.FormatMemoryGrantKB(mg.MaxUsedMemoryKB)} used ({grantPct:N0}%)", grantColor); if (mg.GrantWaitTimeMs > 0) AddRow("Grant wait", $"{mg.GrantWaitTimeMs:N0}ms", "#E57373"); diff --git a/src/PlanViewer.Core/Output/TextFormatter.cs b/src/PlanViewer.Core/Output/TextFormatter.cs index eb7f99e..04ccc80 100644 --- a/src/PlanViewer.Core/Output/TextFormatter.cs +++ b/src/PlanViewer.Core/Output/TextFormatter.cs @@ -62,13 +62,15 @@ public static void WriteText(AnalysisResult result, TextWriter writer) writer.WriteLine($"Runtime: {stmt.QueryTime.ElapsedTimeMs:N0}ms elapsed, {stmt.QueryTime.CpuTimeMs:N0}ms CPU"); if (stmt.MemoryGrant != null && stmt.MemoryGrant.GrantedKB > 0) { - var grantedMB = stmt.MemoryGrant.GrantedKB / 1024.0; - var usedMB = stmt.MemoryGrant.MaxUsedKB / 1024.0; - var pctUsed = grantedMB > 0 ? usedMB / grantedMB * 100 : 0; + var pctUsed = stmt.MemoryGrant.GrantedKB > 0 + ? (double)stmt.MemoryGrant.MaxUsedKB / stmt.MemoryGrant.GrantedKB * 100 : 0; var pctContext = ""; if (result.ServerContext?.MaxServerMemoryMB > 0) + { + var grantedMB = stmt.MemoryGrant.GrantedKB / 1024.0; pctContext = $", {grantedMB / result.ServerContext.MaxServerMemoryMB * 100:N1}% of max server memory"; - writer.WriteLine($"Memory grant: {grantedMB:N1} MB granted, {usedMB:N1} MB used ({pctUsed:N0}% utilized{pctContext})"); + } + writer.WriteLine($"Memory grant: {FormatMemoryGrantKB(stmt.MemoryGrant.GrantedKB)} granted, {FormatMemoryGrantKB(stmt.MemoryGrant.MaxUsedKB)} used ({pctUsed:N0}% utilized{pctContext})"); } // Expensive operators — promoted to right after memory grant. @@ -323,6 +325,21 @@ private static void WriteGroupedOperatorWarnings(List warnings, T } } + /// + /// Formats a memory value given in KB to a human-readable string. + /// Under 1,024 KB: show KB (e.g., "512 KB"). + /// 1,024 KB to 1,048,576 KB: show MB with 1 decimal (e.g., "533.3 MB"). + /// Over 1,048,576 KB: show GB with 2 decimals (e.g., "2.14 GB"). + /// + public static string FormatMemoryGrantKB(long kb) + { + if (kb < 1024) + return $"{kb:N0} KB"; + if (kb < 1024 * 1024) + return $"{kb / 1024.0:N1} MB"; + return $"{kb / (1024.0 * 1024.0):N2} GB"; + } + /// /// Replaces newlines with unit separator (U+001F) so multi-line warning messages /// survive the top-level line split in AdviceContentBuilder.Build().