From 8f057b283bb6525c1bf78fe5831a286e4c3353a1 Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Tue, 30 Jun 2026 14:49:42 +0800 Subject: [PATCH 1/2] illustrate all iterations for gcperfsim --- .../Analysis/GCTraceMetricComparisonResult.cs | 26 +++++++ .../Presentation/GCPerfSim/Markdown.cs | 71 ++++++++++++++----- .../GCPerfSim/GCPerfSimAnalyzeCommand.cs | 2 +- 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs index 8a9ebaf819e..59fe45e3a58 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs @@ -75,6 +75,10 @@ public GCTraceMetricComparisonResult(IEnumerable baselines, IEnu OutliersFreeComparandMetricCollection = API.Statistics.RemoveOutliers(OriginalComparandMetricCollection); // Calculate averaged metrics + OriginalAveragedBaselineMetric = API.GoodLinq.Average( + OriginalBaselineMetricCollection.Where(r => !double.IsNaN(r)), r => r); + OriginalAveragedComparandMetric = API.GoodLinq.Average( + OriginalComparandMetricCollection.Where(r => !double.IsNaN(r)), r => r); AveragedBaselineMetric = API.GoodLinq.Average(OutliersFreeBaselineMetricCollection, r => r); AveragedComparandMetric = API.GoodLinq.Average(OutliersFreeComparandMetricCollection, r => r); } @@ -87,9 +91,30 @@ public GCTraceMetricComparisonResult(IEnumerable baselines, IEnu public IEnumerable OutliersFreeBaselineMetricCollection { get; } public IEnumerable OutliersFreeComparandMetricCollection { get; } + public double OriginalAveragedBaselineMetric { get; } + public double OriginalAveragedComparandMetric { get; } public double AveragedBaselineMetric { get; } public double AveragedComparandMetric { get; } + public double OriginalDelta => OriginalAveragedComparandMetric - OriginalAveragedBaselineMetric; public double Delta => AveragedComparandMetric - AveragedBaselineMetric; + public double OriginalPercentageDelta + { + get + { + if (OriginalAveragedBaselineMetric == 0) + { + if (OriginalAveragedComparandMetric == 0) + { + return 0; + } + else + { + return double.NaN; + } + } + return (OriginalDelta / OriginalAveragedBaselineMetric) * 100.0; + } + } public double PercentageDelta { get @@ -114,6 +139,7 @@ public double PercentageDelta // Regression-oriented delta used for classification: positive => regression, negative => improvement, // regardless of whether the metric is higher-is-better (e.g. Speed_MBPerMSec) or lower-is-better. + public double OriginalRegressionPercentageDelta => MetricDirection.GetRegressionDelta(MetricName, OriginalPercentageDelta); public double RegressionPercentageDelta => MetricDirection.GetRegressionDelta(MetricName, PercentageDelta); } } diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs index ffa12fb7437..5c95fa2bbc0 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs @@ -150,7 +150,7 @@ public static void GenerateComparisonTable(ResultItem baseResultItem, ResultItem } public static void GenerateForAnalyzeCommand(GCPerfSimConfiguration configuration, - IEnumerable comparisonResultsCollection, + IReadOnlyCollection comparisonResultsCollection, Dictionary executionDetails, string path) { @@ -239,63 +239,98 @@ public static void GenerateForAnalyzeCommand(GCPerfSimConfiguration configuratio sw.WriteLine(); sw.AddReproSection(executionDetails); sw.WriteLine(); - sw.AddDetailsOfSingleComparison(configuration, comparisonResults); + sw.AddDetailsOfSingleRun(configuration, comparisonResults); } } } - internal static void AddDetailsOfSingleComparison(this StreamWriter sw, - GCPerfSimConfiguration configuration, - GCTraceMetricComparisonResults comparisonResult) + internal static void AddDetailsOfSingleRun(this StreamWriter sw, + GCPerfSimConfiguration configuration, + GCTraceMetricComparisonResults comparisonResult) { // Large Regressions sw.WriteLine($"### Large Regressions (>= 20%): {comparisonResult.LargeRegressions.Count()} \n"); - sw.AddTableForSingleCriteria(comparisonResult.LargeRegressions); + sw.AddTablesForSingleCriteria(comparisonResult.LargeRegressions); sw.WriteLine("\n"); // Large Improvements sw.WriteLine($"### Large Improvements (<= -20%): {comparisonResult.LargeImprovements.Count()} \n"); - sw.AddTableForSingleCriteria(comparisonResult.LargeImprovements); + sw.AddTablesForSingleCriteria(comparisonResult.LargeImprovements); sw.WriteLine("\n"); // Regressions sw.WriteLine($"### Regressions (>= 5% and < 20%): {comparisonResult.Regressions.Count()} \n"); - sw.AddTableForSingleCriteria(comparisonResult.Regressions); + sw.AddTablesForSingleCriteria(comparisonResult.Regressions); sw.WriteLine("\n"); // Improvements sw.WriteLine($"### Improvements (> -20% and <= -5%): {comparisonResult.Improvements.Count()} \n"); - sw.AddTableForSingleCriteria(comparisonResult.Improvements); + sw.AddTablesForSingleCriteria(comparisonResult.Improvements); sw.WriteLine("\n"); // Stale Regressions sw.WriteLine($"### Stale Regressions (> 0% and < 5%): {comparisonResult.StaleRegressions.Count()} \n"); - sw.AddTableForSingleCriteria(comparisonResult.StaleRegressions); + sw.AddTablesForSingleCriteria(comparisonResult.StaleRegressions); sw.WriteLine("\n"); // Stale Improvements sw.WriteLine($"### Stale Improvements (> -5% and <= 0%): {comparisonResult.StaleImprovements.Count()} \n"); - sw.AddTableForSingleCriteria(comparisonResult.StaleImprovements); + sw.AddTablesForSingleCriteria(comparisonResult.StaleImprovements); sw.WriteLine("\n\n"); } - - internal static void AddTableForSingleCriteria(this StreamWriter sw, - IEnumerable comparisons) + internal static void AddTablesForSingleCriteria(this StreamWriter sw, + IEnumerable comparisons) { if (comparisons.ToList().Count == 0) { sw.WriteLine("No metrics in this category.\n"); return; } - var runName = comparisons.FirstOrDefault()?.RunName; - sw.WriteLine($" | Metric | Base | {runName} | Δ% | Δ |"); + + foreach (var comparison in comparisons) + { + sw.AddTableForSingleMetric(comparison); + sw.WriteLine("\n"); + } + } + + internal static void AddTableForSingleMetric(this StreamWriter sw, + GCTraceMetricComparisonResult comparison) + { + var runName = comparison.RunName; + var metricName = comparison.MetricName; + sw.WriteLine($" | {metricName} | Base | {runName} | Δ% | Δ |"); sw.WriteLine($" | ----- | ---- | ------ | --- | --- |"); - foreach (var r in comparisons) + int maxIterations = Math.Max(comparison.OriginalBaselineMetricCollection.Count(), comparison.OriginalComparandMetricCollection.Count()); + for (int idx = 0; idx < maxIterations; idx++) { - sw.WriteLine($"| {r.MetricName} | {r.AveragedBaselineMetric:N2} | {r.AveragedComparandMetric:N2} | {r.PercentageDelta:N2} | {r.Delta:N2} |"); + string baseRow = $"| iteration.{idx} "; + var baselineValue = comparison.OriginalBaselineMetricCollection?.ElementAtOrDefault(idx) ??double.NaN; + var comparandValue = comparison.OriginalComparandMetricCollection?.ElementAtOrDefault(idx) ?? double.NaN; + var baselineStr = double.IsNaN(baselineValue) || !(comparison.OutliersFreeBaselineMetricCollection?.Contains(baselineValue) ?? false) ? $"**~~{Math.Round(baselineValue, 2)}~~**" : Math.Round(baselineValue, 2).ToString(); + var comparandStr = double.IsNaN(comparandValue) || !(comparison.OutliersFreeComparandMetricCollection?.Contains(comparandValue) ?? false) ? $"**~~{Math.Round(comparandValue, 2)}~~**" : Math.Round(comparandValue, 2).ToString(); + baseRow += $"| {baselineStr} | {comparandStr} | | |"; + sw.WriteLine(baseRow); } + + // Add Original Averaged, Diff and DiffPerc + var originalAveragedBaselineValue = comparison.OriginalAveragedBaselineMetric; + var originalAveragedComparandValue = comparison.OriginalAveragedComparandMetric; + var originalDiff = comparison.OriginalDelta; + var originalDiffPerc = comparison.OriginalRegressionPercentageDelta; + string originalAveragedRow = $"| Average | {Math.Round(originalAveragedBaselineValue, 2)} | {Math.Round(originalAveragedComparandValue, 2)} | {Math.Round(originalDiffPerc, 2)} | {Math.Round(originalDiff, 2)} |"; + sw.WriteLine(originalAveragedRow); + + // Add Corrective Averaged, Diff and DiffPerc + var averagedBaselineValue = comparison.AveragedBaselineMetric; + var averagedComparandValue = comparison.AveragedComparandMetric; + var diff = comparison.Delta; + var diffPerc = comparison.RegressionPercentageDelta; + string averagedRow = $"| Average (Corrected) | {Math.Round(averagedBaselineValue, 2)} | {Math.Round(averagedComparandValue, 2)} | {Math.Round(diffPerc, 2)} | {Math.Round(diff, 2)} |"; + sw.WriteLine(averagedRow); } + internal static GCTraceMetricComparisonResult? GetGCTraceMetricComparisonResultByRunNameAndMetricName(IEnumerable comparisonResultsCollection, string runName, diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimAnalyzeCommand.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimAnalyzeCommand.cs index c47a19d5573..e4c25e8b37c 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimAnalyzeCommand.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure/Commands/GCPerfSim/GCPerfSimAnalyzeCommand.cs @@ -44,7 +44,7 @@ public static IReadOnlyCollection ExecuteAnalysi } public static void Present(GCPerfSimConfiguration configuration, - IEnumerable comparisonResultGroupedByRunName, + IReadOnlyCollection comparisonResultGroupedByRunName, Dictionary executionDetails) { foreach (var format in configuration.Output.Formats) From f08e991725810604ad4190642ca681718dad33bf Mon Sep 17 00:00:00 2001 From: "Vincent Bu (Centific Technologies Inc)" Date: Tue, 30 Jun 2026 15:40:07 +0800 Subject: [PATCH 2/2] update code based on copilot reviews --- .../Analysis/GCTraceMetricComparisonResult.cs | 4 ++-- .../Presentation/GCPerfSim/Markdown.cs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs index 59fe45e3a58..9a77e45acc8 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Analysis/GCTraceMetricComparisonResult.cs @@ -76,9 +76,9 @@ public GCTraceMetricComparisonResult(IEnumerable baselines, IEnu // Calculate averaged metrics OriginalAveragedBaselineMetric = API.GoodLinq.Average( - OriginalBaselineMetricCollection.Where(r => !double.IsNaN(r)), r => r); + OriginalBaselineMetricCollection.ToList().Where(r => !double.IsNaN(r)), r => r); OriginalAveragedComparandMetric = API.GoodLinq.Average( - OriginalComparandMetricCollection.Where(r => !double.IsNaN(r)), r => r); + OriginalComparandMetricCollection.ToList().Where(r => !double.IsNaN(r)), r => r); AveragedBaselineMetric = API.GoodLinq.Average(OutliersFreeBaselineMetricCollection, r => r); AveragedComparandMetric = API.GoodLinq.Average(OutliersFreeComparandMetricCollection, r => r); } diff --git a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs index 5c95fa2bbc0..20b1185774a 100644 --- a/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs +++ b/src/benchmarks/gc/GC.Infrastructure/GC.Infrastructure.Core/Presentation/GCPerfSim/Markdown.cs @@ -281,13 +281,14 @@ internal static void AddDetailsOfSingleRun(this StreamWriter sw, internal static void AddTablesForSingleCriteria(this StreamWriter sw, IEnumerable comparisons) { - if (comparisons.ToList().Count == 0) + var comparisonList = comparisons.ToList(); + if (comparisonList.Count == 0) { sw.WriteLine("No metrics in this category.\n"); return; } - foreach (var comparison in comparisons) + foreach (var comparison in comparisonList) { sw.AddTableForSingleMetric(comparison); sw.WriteLine("\n");