From ad9dcda69043050286ed4b3da6be48f5eccedfb1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 23:56:23 +0000 Subject: [PATCH 1/6] Initial plan From 100b0a311c843dd93d9d02801b5323f370c84094 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:07:04 +0000 Subject: [PATCH 2/6] Add performance monitoring enhancement: runtime metrics, memory profiling, regression detection, and dashboard Co-authored-by: michaelbeale-IL <63321611+michaelbeale-IL@users.noreply.github.com> --- .github/workflows/build.yml | 6 + .github/workflows/test.yml | 8 + src/ACAT.sln | 19 ++ .../ACATTalk/PerformanceMonitor.cs | 41 +++ .../ACAT.Extensions.UI.csproj | 10 + .../Diagnostics/PerformanceDashboard.xaml | 169 ++++++++++++ .../Diagnostics/PerformanceDashboard.xaml.cs | 252 +++++++++++++++++ .../ACATCore.Tests.Performance.csproj | 28 ++ .../MemoryProfilerTests.cs | 108 ++++++++ .../PerformanceRegressionDetectorTests.cs | 219 +++++++++++++++ .../RuntimeMetricsCollectorTests.cs | 129 +++++++++ src/Libraries/ACATCore/ACAT.Core.csproj | 4 + .../Utility/Diagnostics/MemoryProfiler.cs | 181 +++++++++++++ .../Diagnostics/PerformanceBaseline.cs | 146 ++++++++++ .../PerformanceRegressionDetector.cs | 168 ++++++++++++ .../Metrics/RuntimeMetricsCollector.cs | 254 ++++++++++++++++++ 16 files changed, 1742 insertions(+) create mode 100644 src/Extensions/ACAT.Extensions.UI/Diagnostics/PerformanceDashboard.xaml create mode 100644 src/Extensions/ACAT.Extensions.UI/Diagnostics/PerformanceDashboard.xaml.cs create mode 100644 src/Libraries/ACATCore.Tests.Performance/ACATCore.Tests.Performance.csproj create mode 100644 src/Libraries/ACATCore.Tests.Performance/MemoryProfilerTests.cs create mode 100644 src/Libraries/ACATCore.Tests.Performance/PerformanceRegressionDetectorTests.cs create mode 100644 src/Libraries/ACATCore.Tests.Performance/RuntimeMetricsCollectorTests.cs create mode 100644 src/Libraries/ACATCore/Utility/Diagnostics/MemoryProfiler.cs create mode 100644 src/Libraries/ACATCore/Utility/Diagnostics/PerformanceBaseline.cs create mode 100644 src/Libraries/ACATCore/Utility/Diagnostics/PerformanceRegressionDetector.cs create mode 100644 src/Libraries/ACATCore/Utility/Metrics/RuntimeMetricsCollector.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ff8b0cc..0f87ad0a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,6 +67,12 @@ jobs: working-directory: src/ continue-on-error: false + - name: Run Performance Tests + run: | + dotnet test Libraries/ACATCore.Tests.Performance/ACATCore.Tests.Performance.csproj --configuration ${{ matrix.configuration }} --logger "trx;LogFileName=performance-tests.trx" --logger "console;verbosity=normal" --results-directory TestResults + working-directory: src/ + continue-on-error: false + # Publish test results - name: Publish Test Results uses: dorny/test-reporter@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f6a59ff..408e802b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -61,6 +61,14 @@ jobs: --results-directory TestResults/ working-directory: src/ + - name: Run ACATCore.Tests.Performance + run: > + dotnet test Libraries/ACATCore.Tests.Performance/ACATCore.Tests.Performance.csproj + --configuration Debug + --logger "trx;LogFileName=performance-results.trx" + --results-directory TestResults/ + working-directory: src/ + - name: Upload test results uses: actions/upload-artifact@v4 if: always() diff --git a/src/ACAT.sln b/src/ACAT.sln index badb22ed..a9694137 100644 --- a/src/ACAT.sln +++ b/src/ACAT.sln @@ -149,6 +149,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACATCore.Tests.Logging", "L EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACATCore.Tests.Integration", "Libraries\ACATCore.Tests.Integration\ACATCore.Tests.Integration.csproj", "{55D58F6D-68E0-52D6-5909-E5772FA29551}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACATCore.Tests.Performance", "Libraries\ACATCore.Tests.Performance\ACATCore.Tests.Performance.csproj", "{8B3C1A2D-4E5F-6789-ABCD-EF0123456789}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_signed|Any CPU = Debug_signed|Any CPU @@ -1218,6 +1220,23 @@ Global {55D58F6D-68E0-52D6-5909-E5772FA29551}.Release|x64.Build.0 = Release|x64 {55D58F6D-68E0-52D6-5909-E5772FA29551}.Release|x86.ActiveCfg = Release|x64 {55D58F6D-68E0-52D6-5909-E5772FA29551}.Release|x86.Build.0 = Release|x64 + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug_signed|Any CPU.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug_signed|x64.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug_signed|x86.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug_TestGTEC|Any CPU.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug_TestGTEC|x64.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug_TestGTEC|x86.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug|x64.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Debug|x86.ActiveCfg = Debug|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Release_signed|Any CPU.ActiveCfg = Release|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Release_signed|x64.ActiveCfg = Release|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Release_signed|x86.ActiveCfg = Release|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Release|Any CPU.Build.0 = Release|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Release|x64.ActiveCfg = Release|Any CPU + {8B3C1A2D-4E5F-6789-ABCD-EF0123456789}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Applications/ACATTalk/PerformanceMonitor.cs b/src/Applications/ACATTalk/PerformanceMonitor.cs index a88896ee..c0511159 100644 --- a/src/Applications/ACATTalk/PerformanceMonitor.cs +++ b/src/Applications/ACATTalk/PerformanceMonitor.cs @@ -13,6 +13,8 @@ #if PERFORMANCE +using ACAT.Core.Utility.Diagnostics; +using ACAT.Core.Utility.Metrics; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -27,6 +29,8 @@ namespace ACATTalk /// /// Provides performance monitoring and baseline metrics collection /// for ACATTalk application. Only compiled when PERFORMANCE symbol is defined. + /// Integrates , , + /// and from the ACATCore library. /// public static class PerformanceMonitor { @@ -38,6 +42,11 @@ public static class PerformanceMonitor private static Timer _memoryMonitor; private static readonly object _reportLock = new object(); + // ---- ACATCore performance infrastructure ---- + private static readonly RuntimeMetricsCollector _runtimeCollector = new RuntimeMetricsCollector(); + private static readonly MemoryProfiler _memoryProfiler = new MemoryProfiler(); + private static PerformanceRegressionDetector _regressionDetector; + /// /// Metric categories for organizing performance data /// @@ -65,6 +74,19 @@ public static void Initialize() // Monitor memory every 5 seconds _memoryMonitor = new Timer(MonitorMemory, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + // Start the ACATCore runtime metrics collector (5-second interval) + _runtimeCollector.Start(5000); + + // Capture startup memory snapshot + _memoryProfiler.CaptureSnapshot("Startup"); + + // Load baseline (if present) or use defaults + string baselinePath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "ACAT", "performance_baseline.json"); + PerformanceBaselineData baseline = PerformanceBaseline.Load(baselinePath); + _regressionDetector = new PerformanceRegressionDetector(baseline); + LogEvent("PerformanceMonitor", "Performance monitoring initialized"); } @@ -167,6 +189,7 @@ public static void Shutdown() { _applicationLifetime.Stop(); _memoryMonitor?.Dispose(); + _runtimeCollector.Stop(); var process = Process.GetCurrentProcess(); long endWorkingSet = process.WorkingSet64; @@ -177,6 +200,24 @@ public static void Shutdown() RecordMetric("PeakMemoryUsage", _peakWorkingSet / (1024.0 * 1024.0), "MB", MetricCategory.Memory); RecordMetric("MemoryGrowth", (endWorkingSet - _startWorkingSet) / (1024.0 * 1024.0), "MB", MetricCategory.Memory); + // Capture shutdown memory snapshot and check for regressions + MemorySnapshot shutdownSnap = _memoryProfiler.CaptureSnapshot("Shutdown"); + if (_regressionDetector != null) + { + var observations = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["TotalApplicationLifetime"] = _applicationLifetime.Elapsed.TotalSeconds * 1000, + ["PeakWorkingSetMB"] = _peakWorkingSet / (1024.0 * 1024.0), + ["ManagedHeapMB"] = shutdownSnap.ManagedHeapMB + }; + + IReadOnlyList regressions = _regressionDetector.DetectRegressions(observations); + foreach (RegressionResult r in regressions) + { + Debug.WriteLine($"[PerformanceMonitor] {r}"); + } + } + GenerateReport(); } diff --git a/src/Extensions/ACAT.Extensions.UI/ACAT.Extensions.UI.csproj b/src/Extensions/ACAT.Extensions.UI/ACAT.Extensions.UI.csproj index ac67dd59..1789e2eb 100644 --- a/src/Extensions/ACAT.Extensions.UI/ACAT.Extensions.UI.csproj +++ b/src/Extensions/ACAT.Extensions.UI/ACAT.Extensions.UI.csproj @@ -26,6 +26,9 @@ + + PerformanceDashboard.xaml + @@ -141,6 +144,13 @@ + + + + + MSBuild:Compile + Designer + diff --git a/src/Extensions/ACAT.Extensions.UI/Diagnostics/PerformanceDashboard.xaml b/src/Extensions/ACAT.Extensions.UI/Diagnostics/PerformanceDashboard.xaml new file mode 100644 index 00000000..72cf90f7 --- /dev/null +++ b/src/Extensions/ACAT.Extensions.UI/Diagnostics/PerformanceDashboard.xaml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +