clean up and AMD parity check

This commit is contained in:
2026-06-03 18:59:45 -05:00
parent 2ffd237ddd
commit 739b2be4c2
16 changed files with 106 additions and 325 deletions
+7 -17
View File
@@ -25,20 +25,20 @@ public partial class App : Application
{ {
_startupTrace = StartupTracer.Create(); _startupTrace = StartupTracer.Create();
_startupTrace.Mark("App.Constructor.Begin"); _startupTrace.Mark("App.Constructor.Begin");
MeasureStartup("App.WireExceptionHandlers", () => _startupTrace.Measure("App.WireExceptionHandlers", () =>
{ {
UnhandledException += App_UnhandledException; UnhandledException += App_UnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}); });
MeasureStartup("App.InitializeComponent", InitializeComponent); _startupTrace.Measure("App.InitializeComponent", InitializeComponent);
_startupTrace.Mark("App.Constructor.Complete"); _startupTrace.Mark("App.Constructor.Complete");
_startupTrace.Flush(); _startupTrace.Flush();
} }
protected override void OnLaunched(LaunchActivatedEventArgs args) protected override void OnLaunched(LaunchActivatedEventArgs args)
{ {
MeasureStartup("App.OnLaunched", LaunchMainWindow); _startupTrace.Measure("App.OnLaunched", LaunchMainWindow);
} }
private void LaunchMainWindow() private void LaunchMainWindow()
@@ -50,16 +50,16 @@ public partial class App : Application
try try
{ {
_startupTrace.Mark("App.LaunchMainWindow.Begin"); _startupTrace.Mark("App.LaunchMainWindow.Begin");
_serviceProvider = MeasureStartup("App.BuildServiceProvider", () => _serviceProvider = _startupTrace.Measure("App.BuildServiceProvider", () =>
new ServiceCollection().AddAppServices(_startupTrace).BuildServiceProvider()); new ServiceCollection().AddAppServices(_startupTrace).BuildServiceProvider());
MainWindow mainWindow = MeasureStartup("App.CreateMainWindow", () => new MainWindow( MainWindow mainWindow = _startupTrace.Measure("App.CreateMainWindow", () => new MainWindow(
_serviceProvider.GetRequiredService<MainWindowViewModel>(), _serviceProvider.GetRequiredService<MainWindowViewModel>(),
_startupTrace, _startupTrace,
_serviceProvider.GetRequiredService<IMainWindowRuntimeFactory>(), _serviceProvider.GetRequiredService<IMainWindowRuntimeFactory>(),
_serviceProvider)); _serviceProvider));
_window = mainWindow; _window = mainWindow;
MeasureStartup("App.ActivateWindow", mainWindow.Activate); _startupTrace.Measure("App.ActivateWindow", mainWindow.Activate);
MeasureStartup("App.StartMonitoringAfterActivation", mainWindow.StartMonitoringAfterActivation); _startupTrace.Measure("App.StartMonitoringAfterActivation", mainWindow.StartMonitoringAfterActivation);
_startupTrace.Mark("App.LaunchMainWindow.Complete"); _startupTrace.Mark("App.LaunchMainWindow.Complete");
_startupTrace.Flush(); _startupTrace.Flush();
} }
@@ -72,16 +72,6 @@ public partial class App : Application
} }
} }
private void MeasureStartup(string phase, Action action)
{
_startupTrace.Measure(phase, action);
}
private T MeasureStartup<T>(string phase, Func<T> action)
{
return _startupTrace.Measure(phase, action);
}
private static void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs args) private static void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs args)
{ {
WriteExceptionLog("WinUI unhandled exception", args.Exception); WriteExceptionLog("WinUI unhandled exception", args.Exception);
@@ -64,9 +64,9 @@ public sealed partial class MainWindow : Window
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_startupTrace.Mark("MainWindow.Constructor.Begin"); _startupTrace.Mark("MainWindow.Constructor.Begin");
MeasureStartup("MainWindow.InitializeComponent", InitializeComponent); _startupTrace.Measure("MainWindow.InitializeComponent", InitializeComponent);
MainWindowRuntime runtime = MeasureStartup( MainWindowRuntime runtime = _startupTrace.Measure(
"MainWindow.CreateRuntimeServices", "MainWindow.CreateRuntimeServices",
() => runtimeFactory.Create(this, ViewModel, () => Content.XamlRoot, HideShowMainWindow)); () => runtimeFactory.Create(this, ViewModel, () => Content.XamlRoot, HideShowMainWindow));
_appWindow = runtime.AppWindow; _appWindow = runtime.AppWindow;
@@ -78,7 +78,7 @@ public sealed partial class MainWindow : Window
_columnMeasurer = runtime.ColumnMeasurer; _columnMeasurer = runtime.ColumnMeasurer;
_trayIconService.IsMainIconEnabled = ViewModel.MinimizeToTray; _trayIconService.IsMainIconEnabled = ViewModel.MinimizeToTray;
MeasureStartup("MainWindow.ApplySavedDeviceColumnWidth", _columnMeasurer.ApplySavedWidth, () => FormattableString.Invariant($"width={_columnMeasurer.DeviceColumnWidth:F0}")); _startupTrace.Measure("MainWindow.ApplySavedDeviceColumnWidth", _columnMeasurer.ApplySavedWidth, () => FormattableString.Invariant($"width={_columnMeasurer.DeviceColumnWidth:F0}"));
_columnMeasurer.SettleTriggered += (_, _) => UpdateSensorColumnWidths(); _columnMeasurer.SettleTriggered += (_, _) => UpdateSensorColumnWidths();
TryApplyMicaBackdrop(); TryApplyMicaBackdrop();
@@ -86,16 +86,16 @@ public sealed partial class MainWindow : Window
RootGrid.LayoutUpdated += RootGrid_LayoutUpdated; RootGrid.LayoutUpdated += RootGrid_LayoutUpdated;
Bind(ContentGrid, UIElement.IsHitTestVisibleProperty, ViewModel, nameof(ViewModel.IsHardwareInteractionEnabled)); Bind(ContentGrid, UIElement.IsHitTestVisibleProperty, ViewModel, nameof(ViewModel.IsHardwareInteractionEnabled));
MeasureStartup("MainWindow.PopulateMenuSubmenus", PopulateMenuSubmenus); _startupTrace.Measure("MainWindow.PopulateMenuSubmenus", PopulateMenuSubmenus);
MeasureStartup("MainWindow.PopulateSensorHeader", PopulateSensorHeader); _startupTrace.Measure("MainWindow.PopulateSensorHeader", PopulateSensorHeader);
MeasureStartup("MainWindow.AttachPlotView", () => PlotControl.AttachViewModel(ViewModel)); _startupTrace.Measure("MainWindow.AttachPlotView", () => PlotControl.AttachViewModel(ViewModel));
MeasureStartup("MainWindow.RestoreWindowBounds", _placementService.Restore); _startupTrace.Measure("MainWindow.RestoreWindowBounds", _placementService.Restore);
MeasureStartup("MainWindow.MaximizeWindow", _placementService.Maximize); _startupTrace.Measure("MainWindow.MaximizeWindow", _placementService.Maximize);
MeasureStartup("MainWindow.ApplyTheme", ApplyTheme); _startupTrace.Measure("MainWindow.ApplyTheme", ApplyTheme);
MeasureStartup("MainWindow.UpdatePlotLayout", UpdatePlotLayout); _startupTrace.Measure("MainWindow.UpdatePlotLayout", UpdatePlotLayout);
_timer = MeasureStartup("MainWindow.CreateTimer", () => _timer = _startupTrace.Measure("MainWindow.CreateTimer", () =>
{ {
DispatcherQueueTimer timer = DispatcherQueue.CreateTimer(); DispatcherQueueTimer timer = DispatcherQueue.CreateTimer();
timer.Interval = ViewModel.UpdateInterval; timer.Interval = ViewModel.UpdateInterval;
@@ -103,7 +103,7 @@ public sealed partial class MainWindow : Window
return timer; return timer;
}); });
MeasureStartup("MainWindow.WireEvents", () => _startupTrace.Measure("MainWindow.WireEvents", () =>
{ {
ViewModel.PropertyChanged += (_, args) => ViewModel.PropertyChanged += (_, args) =>
{ {
@@ -173,11 +173,11 @@ public sealed partial class MainWindow : Window
{ {
_startupTrace.Mark("MainWindow.StartMonitoringAfterActivation.Begin"); _startupTrace.Mark("MainWindow.StartMonitoringAfterActivation.Begin");
ApplyInitialWindowState(); ApplyInitialWindowState();
await MeasureStartupAsync("MainWindowViewModel.StartAsync", ViewModel.StartAsync); await _startupTrace.MeasureAsync("MainWindowViewModel.StartAsync", ViewModel.StartAsync);
SyncTraySensors(); SyncTraySensors();
SyncGadgetSensors(); SyncGadgetSensors();
UpdateGadgetVisibility(); UpdateGadgetVisibility();
MeasureStartup("MainWindow.StartTimer", _timer.Start); _startupTrace.Measure("MainWindow.StartTimer", _timer.Start);
_startupTrace.Mark("MainWindow.StartMonitoringAfterActivation.Complete"); _startupTrace.Mark("MainWindow.StartMonitoringAfterActivation.Complete");
RequestStartupTraceComplete(); RequestStartupTraceComplete();
_startupTrace.Flush(); _startupTrace.Flush();
@@ -218,47 +218,6 @@ public sealed partial class MainWindow : Window
_startupTrace.Flush(); _startupTrace.Flush();
} }
private void MeasureStartup(string phase, Action action)
{
if (_startupTrace.IsComplete)
{
action();
return;
}
_startupTrace.Measure(phase, action);
}
private void MeasureStartup(string phase, Action action, Func<string> getDetail)
{
if (_startupTrace.IsComplete)
{
action();
return;
}
_startupTrace.Measure(phase, action, getDetail);
}
private T MeasureStartup<T>(string phase, Func<T> action)
{
if (_startupTrace.IsComplete)
return action();
return _startupTrace.Measure(phase, action);
}
private async Task MeasureStartupAsync(string phase, Func<Task> action)
{
if (_startupTrace.IsComplete)
{
await action();
return;
}
await _startupTrace.MeasureAsync(phase, action);
}
private void PopulateMenuSubmenus() private void PopulateMenuSubmenus()
{ {
PopulateRadioSubMenu(TemperatureUnitMenu, [ PopulateRadioSubMenu(TemperatureUnitMenu, [
@@ -547,7 +506,7 @@ public sealed partial class MainWindow : Window
private void RebuildSensorTree() private void RebuildSensorTree()
{ {
MeasureStartup("MainWindow.RebuildSensorTree", RebuildSensorTreeCore, GetRootSizeDetail); _startupTrace.Measure("MainWindow.RebuildSensorTree", RebuildSensorTreeCore, GetRootSizeDetail);
_columnMeasurer.ScheduleSettle(); _columnMeasurer.ScheduleSettle();
SyncTraySensors(); SyncTraySensors();
SyncGadgetSensors(); SyncGadgetSensors();
@@ -842,7 +801,7 @@ public sealed partial class MainWindow : Window
private void UpdateSensorColumnWidths() private void UpdateSensorColumnWidths()
{ {
MeasureStartup("MainWindow.UpdateSensorColumnWidths", _columnMeasurer.UpdateWidths, () => FormattableString.Invariant($"rows={_columnMeasurer.RowCount}, cacheEntries={_columnMeasurer.CacheEntryCount}, deviceWidth={_columnMeasurer.DeviceColumnWidth:F0}, settled={_columnMeasurer.IsSettled}")); _startupTrace.Measure("MainWindow.UpdateSensorColumnWidths", _columnMeasurer.UpdateWidths, () => FormattableString.Invariant($"rows={_columnMeasurer.RowCount}, cacheEntries={_columnMeasurer.CacheEntryCount}, deviceWidth={_columnMeasurer.DeviceColumnWidth:F0}, settled={_columnMeasurer.IsSettled}"));
} }
private TextBlock CreateHeaderText(string text, int column, Visibility visibility, string? bindingPath = null) private TextBlock CreateHeaderText(string text, int column, Visibility visibility, string? bindingPath = null)
@@ -228,34 +228,26 @@ public sealed class HardwareMonitorService : IHardwareMonitorService
ForceDriveWakeup = Settings.GetValue("forceDriveWakeupItem", false); ForceDriveWakeup = Settings.GetValue("forceDriveWakeupItem", false);
} }
private static readonly string[] WinUiDeferredDetectionDefaults =
{
DeferDimmDetectionSetting,
DeferCpuInitialUpdateSetting,
DeferTscEstimationSetting,
DeferNvidiaDetectionSetting,
DeferStorageDetectionSetting,
DeferNetworkDetectionSetting,
DeferIntelGpuDetectionSetting,
DeferControllerDetectionSetting,
DeferPsuDetectionSetting,
};
private void ApplyWinUiHardwareDefaults() private void ApplyWinUiHardwareDefaults()
{ {
if (!Settings.Contains(DeferDimmDetectionSetting)) foreach (string setting in WinUiDeferredDetectionDefaults)
Settings.SetValue(DeferDimmDetectionSetting, true); {
if (!Settings.Contains(setting))
if (!Settings.Contains(DeferCpuInitialUpdateSetting)) Settings.SetValue(setting, true);
Settings.SetValue(DeferCpuInitialUpdateSetting, true); }
if (!Settings.Contains(DeferTscEstimationSetting))
Settings.SetValue(DeferTscEstimationSetting, true);
if (!Settings.Contains(DeferNvidiaDetectionSetting))
Settings.SetValue(DeferNvidiaDetectionSetting, true);
if (!Settings.Contains(DeferStorageDetectionSetting))
Settings.SetValue(DeferStorageDetectionSetting, true);
if (!Settings.Contains(DeferNetworkDetectionSetting))
Settings.SetValue(DeferNetworkDetectionSetting, true);
if (!Settings.Contains(DeferIntelGpuDetectionSetting))
Settings.SetValue(DeferIntelGpuDetectionSetting, true);
if (!Settings.Contains(DeferControllerDetectionSetting))
Settings.SetValue(DeferControllerDetectionSetting, true);
if (!Settings.Contains(DeferPsuDetectionSetting))
Settings.SetValue(DeferPsuDetectionSetting, true);
} }
private void SetHardwareEnabled(string settingName, bool value, Action<bool> setter) private void SetHardwareEnabled(string settingName, bool value, Action<bool> setter)
@@ -99,6 +99,12 @@ internal sealed class FileStartupTracer : IStartupTracer
public void Measure(string phase, Action action, Func<string>? getDetail) public void Measure(string phase, Action action, Func<string>? getDetail)
{ {
if (IsComplete)
{
action();
return;
}
TimeSpan start = _totalStopwatch.Elapsed; TimeSpan start = _totalStopwatch.Elapsed;
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
try try
@@ -117,18 +123,16 @@ internal sealed class FileStartupTracer : IStartupTracer
public T Measure<T>(string phase, Func<T> action) public T Measure<T>(string phase, Func<T> action)
{ {
return Measure(phase, action, null); if (IsComplete)
} return action();
public T Measure<T>(string phase, Func<T> action, Func<T, string>? getDetail)
{
TimeSpan start = _totalStopwatch.Elapsed; TimeSpan start = _totalStopwatch.Elapsed;
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
try try
{ {
T result = action(); T result = action();
stopwatch.Stop(); stopwatch.Stop();
AddEntry(phase, start, stopwatch.Elapsed, "OK", GetDetail(result, getDetail)); AddEntry(phase, start, stopwatch.Elapsed, "OK", "");
return result; return result;
} }
catch (Exception ex) catch (Exception ex)
@@ -141,42 +145,19 @@ internal sealed class FileStartupTracer : IStartupTracer
public async Task MeasureAsync(string phase, Func<Task> action) public async Task MeasureAsync(string phase, Func<Task> action)
{ {
await MeasureAsync(phase, action, null); if (IsComplete)
} {
await action();
return;
}
public async Task MeasureAsync(string phase, Func<Task> action, Func<string>? getDetail)
{
TimeSpan start = _totalStopwatch.Elapsed; TimeSpan start = _totalStopwatch.Elapsed;
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
try try
{ {
await action(); await action();
stopwatch.Stop(); stopwatch.Stop();
AddEntry(phase, start, stopwatch.Elapsed, "OK", GetDetail(getDetail)); AddEntry(phase, start, stopwatch.Elapsed, "OK", "");
}
catch (Exception ex)
{
stopwatch.Stop();
AddEntry(phase, start, stopwatch.Elapsed, "ERROR", $"{ex.GetType().FullName}: {ex.Message}");
throw;
}
}
public async Task<T> MeasureAsync<T>(string phase, Func<Task<T>> action)
{
return await MeasureAsync(phase, action, null);
}
public async Task<T> MeasureAsync<T>(string phase, Func<Task<T>> action, Func<T, string>? getDetail)
{
TimeSpan start = _totalStopwatch.Elapsed;
Stopwatch stopwatch = Stopwatch.StartNew();
try
{
T result = await action();
stopwatch.Stop();
AddEntry(phase, start, stopwatch.Elapsed, "OK", GetDetail(result, getDetail));
return result;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -212,21 +193,6 @@ internal sealed class FileStartupTracer : IStartupTracer
} }
} }
private static string GetDetail<T>(T result, Func<T, string>? getDetail)
{
if (getDetail == null)
return "";
try
{
return getDetail(result) ?? "";
}
catch (Exception ex)
{
return $"Detail unavailable: {ex.GetType().FullName}: {ex.Message}";
}
}
private void AddEntry(string phase, TimeSpan elapsed, string status, string detail) private void AddEntry(string phase, TimeSpan elapsed, string status, string detail)
{ {
AddEntry(phase, _totalStopwatch.Elapsed, elapsed, status, detail); AddEntry(phase, _totalStopwatch.Elapsed, elapsed, status, detail);
@@ -27,13 +27,5 @@ internal interface IStartupTracer : IDisposable
T Measure<T>(string phase, Func<T> action); T Measure<T>(string phase, Func<T> action);
T Measure<T>(string phase, Func<T> action, Func<T, string>? getDetail);
Task MeasureAsync(string phase, Func<Task> action); Task MeasureAsync(string phase, Func<Task> action);
Task MeasureAsync(string phase, Func<Task> action, Func<string>? getDetail);
Task<T> MeasureAsync<T>(string phase, Func<Task<T>> action);
Task<T> MeasureAsync<T>(string phase, Func<Task<T>> action, Func<T, string>? getDetail);
} }
@@ -48,31 +48,11 @@ internal sealed class NoOpStartupTracer : IStartupTracer
return action(); return action();
} }
public T Measure<T>(string phase, Func<T> action, Func<T, string>? getDetail)
{
return action();
}
public Task MeasureAsync(string phase, Func<Task> action) public Task MeasureAsync(string phase, Func<Task> action)
{ {
return action(); return action();
} }
public Task MeasureAsync(string phase, Func<Task> action, Func<string>? getDetail)
{
return action();
}
public Task<T> MeasureAsync<T>(string phase, Func<Task<T>> action)
{
return action();
}
public Task<T> MeasureAsync<T>(string phase, Func<Task<T>> action, Func<T, string>? getDetail)
{
return action();
}
public void Dispose() public void Dispose()
{ {
} }
@@ -873,12 +873,12 @@ public sealed class MainWindowViewModel : ViewModelBase, IDisposable
IsHardwareLoading = true; IsHardwareLoading = true;
_startupTrace.Mark("MainWindowViewModel.StartAsync.Begin"); _startupTrace.Mark("MainWindowViewModel.StartAsync.Begin");
StatusText = "Initializing hardware sensors..."; StatusText = "Initializing hardware sensors...";
await MeasureStartupAsync("MainWindowViewModel.HardwareMonitor.OpenAsync", () => _hardwareMonitor.OpenAsync(raiseTreeRebuilt: false)); await _startupTrace.MeasureAsync("MainWindowViewModel.HardwareMonitor.OpenAsync", () => _hardwareMonitor.OpenAsync(raiseTreeRebuilt: false));
MeasureStartup("MainWindowViewModel.UpdateRoot", UpdateRoot, GetRootDetail); _startupTrace.Measure("MainWindowViewModel.UpdateRoot", UpdateRoot, GetRootDetail);
StatusText = "Reading sensor values..."; StatusText = "Reading sensor values...";
await MeasureStartupAsync("MainWindowViewModel.InitialSensorValueUpdate", () => RefreshSensorValuesAsync(trackPlotPoints: true, logSensors: false)); await _startupTrace.MeasureAsync("MainWindowViewModel.InitialSensorValueUpdate", () => RefreshSensorValuesAsync(trackPlotPoints: true, logSensors: false));
MeasureStartup("MainWindowViewModel.StartWebServerFromSettings", StartWebServerFromSettings); _startupTrace.Measure("MainWindowViewModel.StartWebServerFromSettings", StartWebServerFromSettings);
MeasureStartup("MainWindowViewModel.UpdateStatus", UpdateStatus, GetRootDetail); _startupTrace.Measure("MainWindowViewModel.UpdateStatus", UpdateStatus, GetRootDetail);
_isStarted = true; _isStarted = true;
_startupTrace.Mark("MainWindowViewModel.StartAsync.Complete", GetRootDetail()); _startupTrace.Mark("MainWindowViewModel.StartAsync.Complete", GetRootDetail());
} }
@@ -956,47 +956,6 @@ public sealed class MainWindowViewModel : ViewModelBase, IDisposable
return $"{hardwareCount} hardware device(s), {sensorCount} sensor(s)"; return $"{hardwareCount} hardware device(s), {sensorCount} sensor(s)";
} }
private void MeasureStartup(string phase, Action action)
{
if (_startupTrace.IsComplete)
{
action();
return;
}
_startupTrace.Measure(phase, action);
}
private void MeasureStartup(string phase, Action action, Func<string> getDetail)
{
if (_startupTrace.IsComplete)
{
action();
return;
}
_startupTrace.Measure(phase, action, getDetail);
}
private T MeasureStartup<T>(string phase, Func<T> action)
{
if (_startupTrace.IsComplete)
return action();
return _startupTrace.Measure(phase, action);
}
private async Task MeasureStartupAsync(string phase, Func<Task> action)
{
if (_startupTrace.IsComplete)
{
await action();
return;
}
await _startupTrace.MeasureAsync(phase, action);
}
private void NotifyColumnVisibilityChanged() private void NotifyColumnVisibilityChanged()
{ {
OnPropertyChanged(nameof(ValueColumnVisibility)); OnPropertyChanged(nameof(ValueColumnVisibility));
@@ -1038,11 +997,11 @@ public sealed class MainWindowViewModel : ViewModelBase, IDisposable
private void UpdateRoot() private void UpdateRoot()
{ {
MeasureStartup("MainWindowViewModel.RootItems.Clear", RootItems.Clear); _startupTrace.Measure("MainWindowViewModel.RootItems.Clear", RootItems.Clear);
SensorTreeItemViewModel root = MeasureStartup("MainWindowViewModel.GetHardwareRoot", () => _hardwareMonitor.Root); SensorTreeItemViewModel root = _startupTrace.Measure("MainWindowViewModel.GetHardwareRoot", () => _hardwareMonitor.Root);
MeasureStartup("MainWindowViewModel.ConfigureRoot", () => root.Configure(TemperatureUnit, ShowHiddenSensors, ShowValueColumn, ShowMinColumn, ShowMaxColumn), GetRootDetail); _startupTrace.Measure("MainWindowViewModel.ConfigureRoot", () => root.Configure(TemperatureUnit, ShowHiddenSensors, ShowValueColumn, ShowMinColumn, ShowMaxColumn), GetRootDetail);
MeasureStartup("MainWindowViewModel.RootItems.Add", () => RootItems.Add(root), GetRootDetail); _startupTrace.Measure("MainWindowViewModel.RootItems.Add", () => RootItems.Add(root), GetRootDetail);
MeasureStartup("MainWindowViewModel.ApplySensorValuesTimeWindow", ApplySensorValuesTimeWindow, GetRootDetail); _startupTrace.Measure("MainWindowViewModel.ApplySensorValuesTimeWindow", ApplySensorValuesTimeWindow, GetRootDetail);
RefreshStatusCounts(); RefreshStatusCounts();
} }
+1 -13
View File
@@ -29,6 +29,7 @@ using LibreHardwareMonitor.Hardware.PowerMonitor;
using LibreHardwareMonitor.Hardware.Psu.Corsair; using LibreHardwareMonitor.Hardware.Psu.Corsair;
using LibreHardwareMonitor.Hardware.Psu.Msi; using LibreHardwareMonitor.Hardware.Psu.Msi;
using LibreHardwareMonitor.Hardware.Storage; using LibreHardwareMonitor.Hardware.Storage;
using static LibreHardwareMonitor.Hardware.HardwareStartupTrace;
namespace LibreHardwareMonitor.Hardware; namespace LibreHardwareMonitor.Hardware;
@@ -769,19 +770,6 @@ public class Computer : IComputer
AddMeasuredGroup(startupTrace, "BatteryGroup", () => new BatteryGroup(_settings)); AddMeasuredGroup(startupTrace, "BatteryGroup", () => new BatteryGroup(_settings));
} }
private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action)
{
if (startupTrace != null)
startupTrace.Measure(phase, action);
else
action();
}
private static T Measure<T>(HardwareStartupTrace startupTrace, string phase, Func<T> action)
{
return startupTrace != null ? startupTrace.Measure(phase, action) : action();
}
private void AddMeasuredGroup(HardwareStartupTrace startupTrace, string phase, Func<IGroup> createGroup) private void AddMeasuredGroup(HardwareStartupTrace startupTrace, string phase, Func<IGroup> createGroup)
{ {
Add(Measure(startupTrace, phase, createGroup)); Add(Measure(startupTrace, phase, createGroup));
@@ -93,7 +93,9 @@ internal sealed class Amd0FCpu : AmdCpu
Mutexes.ReleasePciBus(); Mutexes.ReleasePciBus();
} }
if (HasTimeStampCounter) // TimeStampCounterFrequency is 0 while deferred TSC estimation is still pending; skip the clock math until it
// lands so the core/bus clocks keep their previous value instead of momentarily reporting 0 MHz (see IntelCpu).
if (HasTimeStampCounter && TimeStampCounterFrequency > 0)
{ {
double newBusClock = 0; double newBusClock = 0;
@@ -396,7 +396,11 @@ internal sealed class Amd17Cpu : AmdCpu
} }
double timeStampCounterMultiplier = GetTimeStampCounterMultiplier(); double timeStampCounterMultiplier = GetTimeStampCounterMultiplier();
if (timeStampCounterMultiplier > 0)
// When TSC estimation is deferred to the background, TimeStampCounterFrequency is 0 until the estimate
// lands (or GenericCpu.Update self-corrects it from real TSC deltas). Skip the bus-clock math until then so
// the sensor keeps its previous value instead of momentarily reporting 0 MHz (mirrors IntelCpu).
if (timeStampCounterMultiplier > 0 && _cpu.TimeStampCounterFrequency > 0)
{ {
_busClock.Value = (float)(_cpu.TimeStampCounterFrequency / timeStampCounterMultiplier); _busClock.Value = (float)(_cpu.TimeStampCounterFrequency / timeStampCounterMultiplier);
_cpu.ActivateSensor(_busClock); _cpu.ActivateSensor(_busClock);
@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using static LibreHardwareMonitor.Hardware.HardwareStartupTrace;
namespace LibreHardwareMonitor.Hardware.Cpu; namespace LibreHardwareMonitor.Hardware.Cpu;
@@ -41,7 +42,7 @@ internal class CpuGroup : IGroup
_threads[index] = coreThreads; _threads[index] = coreThreads;
GenericCpu cpu = Measure(startupTrace, GenericCpu cpu = Measure(startupTrace,
$"CpuGroup.Processor{index}.{GetCpuConstructorName(threads[0])}", $"CpuGroup.Processor{index}.CreateCpu",
() => CreateCpu(index, coreThreads, settings, startupTrace), () => CreateCpu(index, coreThreads, settings, startupTrace),
DescribeCpu); DescribeCpu);
if (cpu != null) if (cpu != null)
@@ -137,36 +138,6 @@ internal class CpuGroup : IGroup
} }
} }
private static string GetCpuConstructorName(CpuId thread)
{
switch (thread.Vendor)
{
case Vendor.Intel:
return nameof(IntelCpu);
case Vendor.AMD:
switch (thread.Family)
{
case 0x0F:
return nameof(Amd0FCpu);
case 0x10:
case 0x11:
case 0x12:
case 0x14:
case 0x15:
case 0x16:
return "UnsupportedAmdCpu";
case 0x17:
case 0x19:
case 0x1A:
return nameof(Amd17Cpu);
default:
return nameof(GenericCpu);
}
default:
return nameof(GenericCpu);
}
}
private static string DescribeProcessorThreads(CpuId[][] processorThreads) private static string DescribeProcessorThreads(CpuId[][] processorThreads)
{ {
int threadCount = 0; int threadCount = 0;
@@ -178,12 +149,7 @@ internal class CpuGroup : IGroup
private static string DescribeCpu(GenericCpu cpu) private static string DescribeCpu(GenericCpu cpu)
{ {
return cpu != null ? $"{cpu.Name}, {cpu.Sensors.Length} sensor(s)" : "Unsupported CPU family"; return cpu != null ? $"{cpu.GetType().Name}: {cpu.Name}, {cpu.Sensors.Length} sensor(s)" : "Unsupported CPU family";
}
private static T Measure<T>(HardwareStartupTrace startupTrace, string phase, Func<T> action, Func<T, string> getDetail)
{
return startupTrace != null ? startupTrace.Measure(phase, action, getDetail) : action();
} }
private static CpuId[][] GetProcessorThreads() private static CpuId[][] GetProcessorThreads()
@@ -11,6 +11,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using static LibreHardwareMonitor.Hardware.HardwareStartupTrace;
namespace LibreHardwareMonitor.Hardware.Cpu; namespace LibreHardwareMonitor.Hardware.Cpu;
@@ -182,19 +183,6 @@ public class GenericCpu : Hardware
return new Identifier(s, processorIndex.ToString(CultureInfo.InvariantCulture)); return new Identifier(s, processorIndex.ToString(CultureInfo.InvariantCulture));
} }
private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action)
{
if (startupTrace != null)
startupTrace.Measure(phase, action);
else
action();
}
private static T Measure<T>(HardwareStartupTrace startupTrace, string phase, Func<T> action, Func<T, string> getDetail)
{
return startupTrace != null ? startupTrace.Measure(phase, action, getDetail) : action();
}
private static void EstimateTimeStampCounterFrequency(CancellationToken cancellationToken, out double frequency, out double error) private static void EstimateTimeStampCounterFrequency(CancellationToken cancellationToken, out double frequency, out double error)
{ {
// preload the function // preload the function
@@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using LibreHardwareMonitor.PawnIo; using LibreHardwareMonitor.PawnIo;
using static LibreHardwareMonitor.Hardware.HardwareStartupTrace;
namespace LibreHardwareMonitor.Hardware.Cpu; namespace LibreHardwareMonitor.Hardware.Cpu;
@@ -572,19 +573,6 @@ internal sealed class IntelCpu : GenericCpu
return result; return result;
} }
private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action)
{
if (startupTrace != null)
startupTrace.Measure(phase, action);
else
action();
}
private static T Measure<T>(HardwareStartupTrace startupTrace, string phase, Func<T> action)
{
return startupTrace != null ? startupTrace.Measure(phase, action) : action();
}
private static bool ShouldDeferInitialUpdate(ISettings settings) private static bool ShouldDeferInitialUpdate(ISettings settings)
{ {
return SettingsParsing.ShouldDefer(settings, DeferInitialUpdateSetting, DeferInitialUpdateEnvironmentVariable); return SettingsParsing.ShouldDefer(settings, DeferInitialUpdateSetting, DeferInitialUpdateEnvironmentVariable);
@@ -92,6 +92,30 @@ internal sealed class HardwareStartupTrace : IDisposable
AddEntry(phase, TimeSpan.Zero, "SKIPPED", null, reason); AddEntry(phase, TimeSpan.Zero, "SKIPPED", null, reason);
} }
/// <summary>
/// Runs <paramref name="action" /> under <paramref name="trace" />, or directly when tracing is disabled
/// (<paramref name="trace" /> is null). Lets callers measure optional startup phases without repeating a null check.
/// </summary>
public static void Measure(HardwareStartupTrace trace, string phase, Action action)
{
if (trace != null)
trace.Measure(phase, action);
else
action();
}
/// <inheritdoc cref="Measure(HardwareStartupTrace, string, Action)" />
public static T Measure<T>(HardwareStartupTrace trace, string phase, Func<T> action)
{
return trace != null ? trace.Measure(phase, action) : action();
}
/// <inheritdoc cref="Measure(HardwareStartupTrace, string, Action)" />
public static T Measure<T>(HardwareStartupTrace trace, string phase, Func<T> action, Func<T, string> getDetail)
{
return trace != null ? trace.Measure(phase, action, getDetail) : action();
}
private static bool IsEnabled(ISettings settings) private static bool IsEnabled(ISettings settings)
{ {
string settingValue = settings.GetValue(EnabledSetting, "false"); string settingValue = settings.GetValue(EnabledSetting, "false");
@@ -16,6 +16,7 @@ using RAMSPDToolkit.SPD;
using RAMSPDToolkit.SPD.Enums; using RAMSPDToolkit.SPD.Enums;
using RAMSPDToolkit.SPD.Interop.Shared; using RAMSPDToolkit.SPD.Interop.Shared;
using RAMSPDToolkit.Windows.Driver; using RAMSPDToolkit.Windows.Driver;
using static LibreHardwareMonitor.Hardware.HardwareStartupTrace;
namespace LibreHardwareMonitor.Hardware.Memory; namespace LibreHardwareMonitor.Hardware.Memory;
@@ -273,22 +274,4 @@ internal class MemoryGroup : IGroup, IHardwareChanged, IHardwareDiscoveryTask
foreach (Hardware hardware in additions) foreach (Hardware hardware in additions)
HardwareAdded?.Invoke(hardware); HardwareAdded?.Invoke(hardware);
} }
private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action)
{
if (startupTrace != null)
startupTrace.Measure(phase, action);
else
action();
}
private static T Measure<T>(HardwareStartupTrace startupTrace, string phase, Func<T> action)
{
return startupTrace != null ? startupTrace.Measure(phase, action) : action();
}
private static T Measure<T>(HardwareStartupTrace startupTrace, string phase, Func<T> action, Func<T, string> getDetail)
{
return startupTrace != null ? startupTrace.Measure(phase, action, getDetail) : action();
}
} }
+1 -1
View File
@@ -91,7 +91,7 @@ Removed from `AddDeferredGroup`/`AddDeferredGroups` (unreachable since `StartDef
- **`CpuGroup`** was **not** parallelized — only refactored to extract `CreateCpu`. Returning `null` for unsupported AMD families preserves the original "add nothing" behavior. - **`CpuGroup`** was **not** parallelized — only refactored to extract `CreateCpu`. Returning `null` for unsupported AMD families preserves the original "add nothing" behavior.
- **`TreeRebuildCoalescer`** correctly coalesces a burst into ~one rebuild (single debounced worker, re-arms on a late change). - **`TreeRebuildCoalescer`** correctly coalesces a burst into ~one rebuild (single debounced worker, re-arms on a late change).
- **Disposed-`CancellationToken` reads** after cancellation are safe (`IsCancellationRequested` does not throw post-dispose). - **Disposed-`CancellationToken` reads** after cancellation are safe (`IsCancellationRequested` does not throw post-dispose).
- **Startup tracing** is fully no-op when disabled (`HardwareStartupTrace.Create` returns `null`; the `Measure` wrappers and `MainWindowViewModel.MeasureStartup` short-circuit). - **Startup tracing** is fully no-op when disabled (`HardwareStartupTrace.Create` returns `null` and the static `HardwareStartupTrace.Measure` helpers run the action directly; on the WinUI side `NoOpStartupTracer` does the same, and `FileStartupTracer` short-circuits once startup is complete).
- **WinUI tree updates are marshaled**: the VM's `HardwareMonitor_TreeRebuilt` handler marshals to the UI via `DispatcherQueue.TryEnqueue`. (Only `Logger` was unmarshaled — fixed in F3.) - **WinUI tree updates are marshaled**: the VM's `HardwareMonitor_TreeRebuilt` handler marshals to the UI via `DispatcherQueue.TryEnqueue`. (Only `Logger` was unmarshaled — fixed in F3.)
--- ---