From c8378e9ab7f6983e7a4d4784c7e3e5a2d0236f51 Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Mon, 1 Jun 2026 16:22:34 -0500 Subject: [PATCH] Reuse a single TextBlock for sensor-column text measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MeasureText runs for every sensor's Value/Min/Max on each update tick, and the frequently-changing value strings miss the width cache, so it created and threw away a WinUI TextBlock (with a native peer) on nearly every call. Reuse one cached instance to avoid that per-tick allocation churn. Not a leak fix — the GC reclaimed those elements fine; this just removes needless allocation and CPU work from the update loop. Co-Authored-By: Claude Opus 4.8 --- LibreHardwareMonitor.Windows.WinUI/MainWindow.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs b/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs index 2d57572..4923833 100644 --- a/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs +++ b/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs @@ -59,6 +59,7 @@ public sealed class MainWindow : Window private readonly WinUiStartupTrace? _startupTrace; private readonly TrayIconService _trayIconService; private readonly Dictionary<(string Text, bool Bold), double> _textMeasurementCache = new(); + private TextBlock? _measurementTextBlock; private readonly List _sensorRowGrids = []; private readonly double[] _sensorColumnWidths = [DefaultSensorColumnWidth, 120, 120, 120]; private PlotWindow? _plotWindow; @@ -1646,13 +1647,14 @@ public sealed class MainWindow : Window if (_textMeasurementCache.Count >= MaxTextMeasurementCacheEntries) _textMeasurementCache.Clear(); - TextBlock textBlock = new() - { - Text = text, - FontWeight = new global::Windows.UI.Text.FontWeight { Weight = bold ? (ushort)600 : (ushort)400 } - }; - textBlock.Measure(new global::Windows.Foundation.Size(double.PositiveInfinity, double.PositiveInfinity)); - width = textBlock.DesiredSize.Width; + // Reuse a single TextBlock for measurement instead of allocating one per cache miss. This runs for every + // sensor's Value/Min/Max on each update tick, and the frequently-changing value strings miss the cache, so the + // old code created and discarded a WinUI element (with a native peer) on nearly every call — avoidable churn. + _measurementTextBlock ??= new TextBlock(); + _measurementTextBlock.Text = text; + _measurementTextBlock.FontWeight = new global::Windows.UI.Text.FontWeight { Weight = bold ? (ushort)600 : (ushort)400 }; + _measurementTextBlock.Measure(new global::Windows.Foundation.Size(double.PositiveInfinity, double.PositiveInfinity)); + width = _measurementTextBlock.DesiredSize.Width; _textMeasurementCache[key] = width; return width; }