From eb2b3cf075e6977bb5cd27ca7653d3a7d8ce2034 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Fri, 15 Dec 2023 15:26:59 -0500 Subject: [PATCH 1/4] Fixes #2319 --- src/BenchmarkDotNet/Running/PowerManagementApplier.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs index f48d95d61e..c4b4a884ea 100644 --- a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs +++ b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs @@ -76,6 +76,14 @@ private void ApplyPlanByGuid(Guid guid) isInitialized = true; } + Guid ultimatePerformanceGuid = PowerPlansDict[PowerPlan.UltimatePerformance]; + Guid highPerformanceGuid = PowerPlansDict[PowerPlan.HighPerformance]; + if (userCurrentPowerPlan == ultimatePerformanceGuid && guid == highPerformanceGuid) + { + logger.WriteLineInfo($"Current power plan is already Ultimate Performance. Not changing to High Performance."); + return; + } + if (PowerManagementHelper.Set(guid)) { powerPlanChanged = true; @@ -91,4 +99,4 @@ private void ApplyPlanByGuid(Guid guid) } } } -} +} \ No newline at end of file From 1d7f905be78ec573a69059898f4e16d91beebf04 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Wed, 16 Jul 2025 16:25:35 -0400 Subject: [PATCH 2/4] Revert code. Add tests --- .../Running/PowerManagementApplier.cs | 8 --- .../PowerManagementApplierTests.cs | 65 ++++++++++++++++++- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs index c4b4a884ea..3338fe877b 100644 --- a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs +++ b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs @@ -76,14 +76,6 @@ private void ApplyPlanByGuid(Guid guid) isInitialized = true; } - Guid ultimatePerformanceGuid = PowerPlansDict[PowerPlan.UltimatePerformance]; - Guid highPerformanceGuid = PowerPlansDict[PowerPlan.HighPerformance]; - if (userCurrentPowerPlan == ultimatePerformanceGuid && guid == highPerformanceGuid) - { - logger.WriteLineInfo($"Current power plan is already Ultimate Performance. Not changing to High Performance."); - return; - } - if (PowerManagementHelper.Set(guid)) { powerPlanChanged = true; diff --git a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs index 69eed9a234..3d9bee5087 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs @@ -12,7 +12,9 @@ public class PowerManagementApplierTests : BenchmarkTestExecutor { public const string HighPerformancePlanGuid = "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"; - public PowerManagementApplierTests(ITestOutputHelper output) : base(output) { } + public PowerManagementApplierTests(ITestOutputHelper output) : base(output) + { + } [FactEnvSpecific("Setting high-performance plan is suitable only on Windows", EnvRequirement.WindowsOnly)] public void TestSettingAndRevertingBackGuid() @@ -42,5 +44,64 @@ public void TestPowerPlanShouldNotChange() Assert.Equal(userPlan, PowerManagementHelper.CurrentPlan); } + + [FactEnvSpecific("Should not change power plan to High Performance if it is already on Ultimate Performance", EnvRequirement.WindowsOnly)] + public void ShouldNotSwitchFromUltimateToHighPerformanceIfHighPerformanceIsMissing() + { + var ultimateGuid = PowerManagementApplier.Map(PowerPlan.UltimatePerformance); + var highGuid = PowerManagementApplier.Map(PowerPlan.HighPerformance); + var userPlan = PowerManagementHelper.CurrentPlan; + + var logger = new OutputLogger(Output); + var powerManagementApplier = new PowerManagementApplier(logger); + + PowerManagementHelper.Set(ultimateGuid); + + powerManagementApplier.ApplyPerformancePlan(highGuid); + + Assert.Equal(ultimateGuid.ToString(), PowerManagementHelper.CurrentPlan.ToString()); + + PowerManagementHelper.Set(userPlan.Value); + powerManagementApplier.Dispose(); + } + + [FactEnvSpecific("Should change to High Performance if user requests it and High Performance plan is present, even if currently on Ultimate Performance", EnvRequirement.WindowsOnly)] + public void ShouldSwitchToHighPerformanceIfPresentWhenRequestedEvenIfOnUltimate() + { + var ultimateGuid = PowerManagementApplier.Map(PowerPlan.UltimatePerformance); + var highGuid = PowerManagementApplier.Map(PowerPlan.HighPerformance); + var userPlan = PowerManagementHelper.CurrentPlan; + + var logger = new OutputLogger(Output); + var powerManagementApplier = new PowerManagementApplier(logger); + + PowerManagementHelper.Set(ultimateGuid); + + powerManagementApplier.ApplyPerformancePlan(highGuid); + + Assert.Equal(highGuid.ToString(), PowerManagementHelper.CurrentPlan.ToString()); + + PowerManagementHelper.Set(userPlan.Value); + powerManagementApplier.Dispose(); + } + + [FactEnvSpecific("Should not change plan if already High Performance", EnvRequirement.WindowsOnly)] + public void ShouldNotChangeIfAlreadyHighPerformance() + { + var highGuid = PowerManagementApplier.Map(PowerPlan.HighPerformance); + var userPlan = PowerManagementHelper.CurrentPlan; + + var logger = new OutputLogger(Output); + var powerManagementApplier = new PowerManagementApplier(logger); + + PowerManagementHelper.Set(highGuid); + + powerManagementApplier.ApplyPerformancePlan(highGuid); + + Assert.Equal(highGuid.ToString(), PowerManagementHelper.CurrentPlan.ToString()); + + PowerManagementHelper.Set(userPlan.Value); + powerManagementApplier.Dispose(); + } } -} +} \ No newline at end of file From 632e769209192c29fe789da0140682fb26e5d337 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Tue, 22 Jul 2025 15:28:35 -0400 Subject: [PATCH 3/4] Added code fix --- .../Helpers/PowerManagementHelper.cs | 34 +++++++++++++++++-- .../Running/PowerManagementApplier.cs | 16 +++++++++ .../PowerManagementApplierTests.cs | 24 +------------ 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs b/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs index e3d82dd517..6e3b8f7e43 100644 --- a/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs +++ b/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; @@ -50,7 +51,7 @@ internal static string CurrentPlanFriendlyName internal static bool Set(Guid newPolicy) { - return PowerSetActiveScheme(IntPtr.Zero, ref newPolicy) == 0; + return PowerSetActiveScheme(IntPtr.Zero, ref newPolicy) == 0; } [DllImport("powrprof.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] @@ -61,5 +62,34 @@ internal static bool Set(Guid newPolicy) [DllImport("powrprof.dll", ExactSpelling = true)] private static extern uint PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid); + + [DllImport("powrprof.dll", SetLastError = true)] + private static extern uint PowerEnumerate(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingsGuid, uint AccessFlags, uint Index, ref Guid Buffer, ref uint BufferSize); + + internal static IEnumerable EnumerateAllPlanGuids() + { + const uint ACCESS_SCHEME = 16; + uint index = 0; + while (true) + { + Guid schemeGuid = Guid.Empty; + uint size = (uint)Marshal.SizeOf(typeof(Guid)); + uint res = PowerEnumerate(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ACCESS_SCHEME, index, ref schemeGuid, ref size); + if (res != 0) + break; + yield return schemeGuid; + index++; + } + } + + internal static bool PlanExists(Guid planGuid) + { + foreach (var guid in EnumerateAllPlanGuids()) + { + if (guid == planGuid) + return true; + } + return false; + } } -} +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs index 3338fe877b..69ede4a22d 100644 --- a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs +++ b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs @@ -76,6 +76,20 @@ private void ApplyPlanByGuid(Guid guid) isInitialized = true; } + Guid currentActivePlan = (Guid)PowerManagementHelper.CurrentPlan; + Guid ultimatePerformanceGuid = PowerPlansDict[PowerPlan.UltimatePerformance]; + Guid highPerformanceGuid = PowerPlansDict[PowerPlan.HighPerformance]; + + if (currentActivePlan == ultimatePerformanceGuid && guid == highPerformanceGuid) + { + if (!PowerManagementHelper.PlanExists(highPerformanceGuid)) + { + logger.WriteLineInfo("Cannot setup High Performance power plan - plan doesn't exist on this system."); + return; + } + logger.WriteLineInfo("Changing from Ultimate Performance to explicitly configured High Performance plan."); + } + if (PowerManagementHelper.Set(guid)) { powerPlanChanged = true; @@ -83,7 +97,9 @@ private void ApplyPlanByGuid(Guid guid) logger.WriteLineInfo($"Setup power plan (GUID: {guid} FriendlyName: {powerPlanFriendlyName})"); } else + { logger.WriteLineError($"Cannot setup power plan (GUID: {guid})"); + } } catch (Exception ex) { diff --git a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs index 3d9bee5087..dd123b2d7b 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs @@ -12,9 +12,7 @@ public class PowerManagementApplierTests : BenchmarkTestExecutor { public const string HighPerformancePlanGuid = "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"; - public PowerManagementApplierTests(ITestOutputHelper output) : base(output) - { - } + public PowerManagementApplierTests(ITestOutputHelper output) : base(output) { } [FactEnvSpecific("Setting high-performance plan is suitable only on Windows", EnvRequirement.WindowsOnly)] public void TestSettingAndRevertingBackGuid() @@ -45,26 +43,6 @@ public void TestPowerPlanShouldNotChange() Assert.Equal(userPlan, PowerManagementHelper.CurrentPlan); } - [FactEnvSpecific("Should not change power plan to High Performance if it is already on Ultimate Performance", EnvRequirement.WindowsOnly)] - public void ShouldNotSwitchFromUltimateToHighPerformanceIfHighPerformanceIsMissing() - { - var ultimateGuid = PowerManagementApplier.Map(PowerPlan.UltimatePerformance); - var highGuid = PowerManagementApplier.Map(PowerPlan.HighPerformance); - var userPlan = PowerManagementHelper.CurrentPlan; - - var logger = new OutputLogger(Output); - var powerManagementApplier = new PowerManagementApplier(logger); - - PowerManagementHelper.Set(ultimateGuid); - - powerManagementApplier.ApplyPerformancePlan(highGuid); - - Assert.Equal(ultimateGuid.ToString(), PowerManagementHelper.CurrentPlan.ToString()); - - PowerManagementHelper.Set(userPlan.Value); - powerManagementApplier.Dispose(); - } - [FactEnvSpecific("Should change to High Performance if user requests it and High Performance plan is present, even if currently on Ultimate Performance", EnvRequirement.WindowsOnly)] public void ShouldSwitchToHighPerformanceIfPresentWhenRequestedEvenIfOnUltimate() { From 0619dc59843ba8804ab3cfd3fff13179e5b0ae81 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Thu, 24 Jul 2025 15:46:06 -0400 Subject: [PATCH 4/4] Adjust runner to keep Ultimate power plan if power plan is not specified --- .../Running/BenchmarkRunnerClean.cs | 20 +++++++- .../PowerManagementApplierTests.cs | 46 +++++++++---------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs index 8ed930c1f4..e461fe89be 100644 --- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs +++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs @@ -232,8 +232,24 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo, { var benchmark = benchmarks[i]; - powerManagementApplier.ApplyPerformancePlan(benchmark.Job.Environment.PowerPlanMode - ?? benchmark.Job.ResolveValue(EnvironmentMode.PowerPlanModeCharacteristic, EnvironmentResolver.Instance).GetValueOrDefault()); + bool userExplicitlySetPlan = benchmark.Job.HasValue(EnvironmentMode.PowerPlanModeCharacteristic); + + var requestedPlan = benchmark.Job.Environment.PowerPlanMode + ?? benchmark.Job.ResolveValue(EnvironmentMode.PowerPlanModeCharacteristic, EnvironmentResolver.Instance).GetValueOrDefault(); + + // If the plan is not explicitly set and would downgrade Ultimate, skip changing + if (!userExplicitlySetPlan && requestedPlan == PowerManagementApplier.Map(PowerPlan.HighPerformance)) + { + var ultimateGuid = PowerManagementApplier.Map(PowerPlan.UltimatePerformance); + var currentPlan = PowerManagementHelper.CurrentPlan; + if (currentPlan.HasValue && currentPlan.Value == ultimateGuid) + { + logger.WriteLineInfo("Already on Ultimate Performance; not switching to High Performance since no plan explicitly set in Job."); + continue; + } + } + + powerManagementApplier.ApplyPerformancePlan(requestedPlan); var info = buildResults[benchmark]; var buildResult = info.buildResult; diff --git a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs index dd123b2d7b..a6cbdad72f 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs @@ -1,5 +1,7 @@ -using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Environments; using BenchmarkDotNet.Helpers; +using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; using BenchmarkDotNet.Tests.Loggers; using BenchmarkDotNet.Tests.XUnit; @@ -43,43 +45,37 @@ public void TestPowerPlanShouldNotChange() Assert.Equal(userPlan, PowerManagementHelper.CurrentPlan); } - [FactEnvSpecific("Should change to High Performance if user requests it and High Performance plan is present, even if currently on Ultimate Performance", EnvRequirement.WindowsOnly)] - public void ShouldSwitchToHighPerformanceIfPresentWhenRequestedEvenIfOnUltimate() + [FactEnvSpecific("Should keep power plan at Ultimate Performance if current power plan is Ultimate when a power plan is not specifically set", EnvRequirement.WindowsOnly)] + public void TestKeepingUltimatePowerPlan() { var ultimateGuid = PowerManagementApplier.Map(PowerPlan.UltimatePerformance); - var highGuid = PowerManagementApplier.Map(PowerPlan.HighPerformance); var userPlan = PowerManagementHelper.CurrentPlan; - var logger = new OutputLogger(Output); - var powerManagementApplier = new PowerManagementApplier(logger); + if (!PowerManagementHelper.PlanExists(ultimateGuid)) + { + Output.WriteLine("Ultimate Performance plan does not exist or cannot be activated. Skipping test."); + return; + } PowerManagementHelper.Set(ultimateGuid); - powerManagementApplier.ApplyPerformancePlan(highGuid); + var job = Job.Default; - Assert.Equal(highGuid.ToString(), PowerManagementHelper.CurrentPlan.ToString()); + var config = ManualConfig.CreateEmpty().AddJob(job); + + BenchmarkRunner.Run(config); + + Assert.Equal(ultimateGuid.ToString(), PowerManagementHelper.CurrentPlan.ToString()); PowerManagementHelper.Set(userPlan.Value); - powerManagementApplier.Dispose(); } - [FactEnvSpecific("Should not change plan if already High Performance", EnvRequirement.WindowsOnly)] - public void ShouldNotChangeIfAlreadyHighPerformance() + public class DummyBenchmark { - var highGuid = PowerManagementApplier.Map(PowerPlan.HighPerformance); - var userPlan = PowerManagementHelper.CurrentPlan; - - var logger = new OutputLogger(Output); - var powerManagementApplier = new PowerManagementApplier(logger); - - PowerManagementHelper.Set(highGuid); - - powerManagementApplier.ApplyPerformancePlan(highGuid); - - Assert.Equal(highGuid.ToString(), PowerManagementHelper.CurrentPlan.ToString()); - - PowerManagementHelper.Set(userPlan.Value); - powerManagementApplier.Dispose(); + [BenchmarkDotNet.Attributes.Benchmark] + public void DoNothing() + { + } } } } \ No newline at end of file