Skip to content

Commit

Permalink
Comparing power plans (#68)
Browse files Browse the repository at this point in the history
Cosmetic fixes (#68)
  • Loading branch information
MarekM25 committed Jan 31, 2019
1 parent 2dd626c commit f3a51c2
Show file tree
Hide file tree
Showing 18 changed files with 115 additions and 73 deletions.
4 changes: 2 additions & 2 deletions docs/articles/configs/powerplans.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ name: Power Plans

# Power Plans

BenchmarkDotNet forces Windows OS to execute on the High-Performance power plan. You can disable this feature by setting the HighPerofrmancePowerPlan flag to false. You can see it in the @BenchmarkDotNet.Samples.IntroPowerPlan.
BenchmarkDotNet forces Windows OS to execute on the High-Performance power plan. You can disable this feature by modify PowerPlanMode property. You can see it in the @BenchmarkDotNet.Samples.IntroPowerPlan.

Please note. During an execution, BenchmarkDotNet saves the current power plan and applies it according to the HighPerformancePowerPlan flag. When all of the benchmarks finish, a previous power plan comes back. However, if someone killed process or energy was plugged off, we could stay with the High-Performance power plan. In this situation, we should return it manually in Windows Control Panel or by powercfg command.
Please note. During an execution, BenchmarkDotNet saves the current power plan and applies it according to the PowerPlanMode property. When all of the benchmarks finish, a previous power plan comes back. However, if someone killed process or energy was plugged off, we could stay with the High-Performance power plan. In this situation, we should return it manually in Windows Control Panel or by powercfg command.

### Links

Expand Down
34 changes: 24 additions & 10 deletions samples/BenchmarkDotNet.Samples/IntroPowerPlan.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;

namespace BenchmarkDotNet.Samples
{
[HighPerformancePowerPlan(false)]
public class IntroPowerPlanDisabled
[Config(typeof(Config))]
public class IntroPowerPlan
{
private class Config : ManualConfig
{
public Config()
{
Add(Job.MediumRun.WithPowerPlan(PowerPlan.HighPerformance));
Add(Job.MediumRun.WithPowerPlan(PowerPlan.UserPowerPlan));
}
}

[Benchmark]
public int SplitJoin()
=> string.Join(",", new string[1000]).Split(',').Length;
}
public int IterationTest()
{
int j = 0;
for (int i = 0; i < short.MaxValue; ++i)
{
j = i;
}

return j;
}

// By default benchmark.net uses high-performance power plan.
// There is no need to set it to true explicitly
[HighPerformancePowerPlan(true)]
public class IntroPowerPlanEnabled
{
[Benchmark]
public int SplitJoin()
=> string.Join(",", new string[1000]).Split(',').Length;
Expand Down

This file was deleted.

1 change: 0 additions & 1 deletion src/BenchmarkDotNet/Configs/ConfigExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public static IDiagnoser GetCompositeDiagnoser(this IConfig config, BenchmarkCas
/// determines if all auto-generated files should be kept or removed after running the benchmarks
/// </summary>
///
[PublicAPI] public static IConfig WithHighPerformancePowerPlan(this IConfig config, bool highPerformancePowerPlan = true) => config.With(c => c.HighPerformancePowerPlan = highPerformancePowerPlan);
[PublicAPI] public static IConfig KeepBenchmarkFiles(this IConfig config, bool value = true) => config.With(m => m.KeepBenchmarkFiles = value);
[PublicAPI] public static IConfig RemoveBenchmarkFiles(this IConfig config) => config.KeepBenchmarkFiles(false);
[PublicAPI] public static IConfig WithArtifactsPath(this IConfig config, string artifactsPath) => config.With(m => m.ArtifactsPath = artifactsPath);
Expand Down
1 change: 0 additions & 1 deletion src/BenchmarkDotNet/Configs/DebugConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ public abstract class DebugConfig : IConfig
public bool SummaryPerType => false;
public string ArtifactsPath => Path.Combine(Directory.GetCurrentDirectory(), "BenchmarkDotNet.Artifacts");
public Encoding Encoding => Encoding.ASCII;
public bool HighPerformancePowerPlan => true;
public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => Array.Empty<BenchmarkLogicalGroupRule>();
public bool StopOnFirstError => false;
}
Expand Down
2 changes: 0 additions & 2 deletions src/BenchmarkDotNet/Configs/DefaultConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ public IEnumerable<IValidator> GetValidators()

public Encoding Encoding => Encoding.ASCII;

public bool HighPerformancePowerPlan => true;

public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => Array.Empty<BenchmarkLogicalGroupRule>();

public bool StopOnFirstError => false;
Expand Down
5 changes: 0 additions & 5 deletions src/BenchmarkDotNet/Configs/IConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ public interface IConfig
/// </summary>
Encoding Encoding { get; }

/// <summary>
/// the default value is true
/// </summary>
bool HighPerformancePowerPlan { get; }

IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules();

bool StopOnFirstError { get; }
Expand Down
2 changes: 0 additions & 2 deletions src/BenchmarkDotNet/Configs/ManualConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ public class ManualConfig : IConfig
[PublicAPI] public bool SummaryPerType { get; set; } = true;
[PublicAPI] public string ArtifactsPath { get; set; }
[PublicAPI] public Encoding Encoding { get; set; }
[PublicAPI] public bool HighPerformancePowerPlan { get; set; } = true;

public void Add(params IColumn[] newColumns) => columnProviders.AddRange(newColumns.Select(c => c.ToProvider()));
public void Add(params IColumnProvider[] newColumnProviders) => columnProviders.AddRange(newColumnProviders);
Expand Down Expand Up @@ -83,7 +82,6 @@ public void Add(IConfig config)
SummaryPerType &= config.SummaryPerType;
ArtifactsPath = config.ArtifactsPath ?? ArtifactsPath;
Encoding = config.Encoding ?? Encoding;
HighPerformancePowerPlan &= config.HighPerformancePowerPlan;
summaryStyle = summaryStyle ?? config.GetSummaryStyle();
logicalGroupRules.AddRange(config.GetLogicalGroupRules());
StopOnFirstError |= config.StopOnFirstError;
Expand Down
2 changes: 0 additions & 2 deletions src/BenchmarkDotNet/Configs/ReadOnlyConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ public ReadOnlyConfig([NotNull] IConfig config)

public Encoding Encoding => config.Encoding;

public bool HighPerformancePowerPlan => config.HighPerformancePowerPlan;

public IEnumerable<BenchmarkLogicalGroupRule> GetLogicalGroupRules() => config.GetLogicalGroupRules();

public bool StopOnFirstError => config.StopOnFirstError;
Expand Down
8 changes: 8 additions & 0 deletions src/BenchmarkDotNet/Environments/PowerPlan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace BenchmarkDotNet.Environments
{
public enum PowerPlan
{
HighPerformance,
UserPowerPlan,
}
}
1 change: 0 additions & 1 deletion src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ namespace BenchmarkDotNet.Helpers
{
internal class PowerManagementHelper
{
internal const string HighPerformanceGuid = "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c";
private const uint ErrorMoreData = 234;
private const uint SuccessCode = 0;

Expand Down
9 changes: 9 additions & 0 deletions src/BenchmarkDotNet/Jobs/EnvironmentMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public sealed class EnvironmentMode : JobMode<EnvironmentMode>
public static readonly Characteristic<IntPtr> AffinityCharacteristic = CreateCharacteristic<IntPtr>(nameof(Affinity));
public static readonly Characteristic<GcMode> GcCharacteristic = CreateCharacteristic<GcMode>(nameof(Gc));
public static readonly Characteristic<IReadOnlyList<EnvironmentVariable>> EnvironmentVariablesCharacteristic = CreateCharacteristic<IReadOnlyList<EnvironmentVariable>>(nameof(EnvironmentVariables));
public static readonly Characteristic<PowerPlanMode> PowerPlanModeCharacteristic = CreateCharacteristic<PowerPlanMode>(nameof(PowerPlanMode));

public static readonly EnvironmentMode Clr = new EnvironmentMode(Runtime.Clr).Freeze();
public static readonly EnvironmentMode Core = new EnvironmentMode(Runtime.Core).Freeze();
Expand Down Expand Up @@ -42,6 +43,7 @@ [PublicAPI] public EnvironmentMode(string id, Jit jit, Platform platform) : this
[PublicAPI] public EnvironmentMode(string id) : base(id)
{
GcCharacteristic[this] = new GcMode();
PowerPlanModeCharacteristic[this] = new PowerPlanMode();
}

/// <summary>
Expand Down Expand Up @@ -92,5 +94,12 @@ public IReadOnlyList<EnvironmentVariable> EnvironmentVariables
set => EnvironmentVariablesCharacteristic[this] = value;
}


public PowerPlanMode PowerPlanMode
{
get => PowerPlanModeCharacteristic[this];
set => PowerPlanModeCharacteristic[this] = value;
}

}
}
7 changes: 7 additions & 0 deletions src/BenchmarkDotNet/Jobs/JobExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ public static Job WithHeapAffinitizeMask(this Job job, int heapAffinitizeMask) =
/// </summary>
public static Job WithMaxIterationCount(this Job job, int count) => job.WithCore(j => j.Run.MaxIterationCount = count);

/// <summary>
/// Power plan for benchmarks.
/// The default value is HighPerformance.
/// <remarks>Only available for Windows.</remarks>
/// </summary>
public static Job WithPowerPlan(this Job job, PowerPlan powerPlan) => job.WithCore(j => j.Environment.PowerPlanMode.PowerPlan = powerPlan);

// Infrastructure
public static Job With(this Job job, IToolchain toolchain) => job.WithCore(j => j.Infrastructure.Toolchain = toolchain);
[PublicAPI] public static Job With(this Job job, IClock clock) => job.WithCore(j => j.Infrastructure.Clock = clock);
Expand Down
28 changes: 28 additions & 0 deletions src/BenchmarkDotNet/Jobs/PowerPlanMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using BenchmarkDotNet.Characteristics;
using BenchmarkDotNet.Environments;

namespace BenchmarkDotNet.Jobs
{
public sealed class PowerPlanMode : JobMode<PowerPlanMode>
{
public static readonly Characteristic<PowerPlan> PowerPlanCharacteristic = CreateCharacteristic<PowerPlan>(nameof(PowerPlan));

public PowerPlanMode() : this(null)
{
}

private PowerPlanMode(string id) : base(id)
{
}

public PowerPlan PowerPlan
{
get { return PowerPlanCharacteristic[this]; }
set { PowerPlanCharacteristic[this] = value; }
}

public bool Equals(PowerPlanMode other)
=> other != null
&& other.PowerPlan == PowerPlan;
}
}
6 changes: 3 additions & 3 deletions src/BenchmarkDotNet/Running/BenchmarkRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ [PublicAPI] public static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos, [C
using (var logStreamWriter = StreamWriter.FromPath(Path.Combine(rootArtifactsFolderPath, title + ".log")))
{
var logger = new CompositeLogger(commonSettingsConfig.GetCompositeLogger(), new StreamLogger(logStreamWriter));
var powerManagementApplier = new PowerManagementApplier(logger);

var supportedBenchmarks = GetSupportedBenchmarks(benchmarkRunInfos, logger, resolver);

Expand Down Expand Up @@ -109,7 +108,6 @@ [PublicAPI] public static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos, [C

foreach (var benchmarkRunInfo in supportedBenchmarks) // we run them in the old order now using the new build artifacts
{
powerManagementApplier.ApplyPerformancePlan(benchmarkRunInfo.Config.HighPerformancePowerPlan);
var runChronometer = Chronometer.Start();

var summary = Run(benchmarkRunInfo, benchmarkToBuildResult, resolver, logger, artifactsToCleanup, rootArtifactsFolderPath, ref runChronometer);
Expand Down Expand Up @@ -137,7 +135,6 @@ [PublicAPI] public static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos, [C
}
finally
{
powerManagementApplier.ApplyUserPowerPlan(logger);
logger.WriteLineHeader("// * Artifacts cleanup *");
Cleanup(new HashSet<string>(artifactsToCleanup.Distinct()));
}
Expand Down Expand Up @@ -170,13 +167,15 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
var config = benchmarkRunInfo.Config;
var reports = new List<BenchmarkReport>();
string title = GetTitle(new[] { benchmarkRunInfo });
var powerManagementApplier = new PowerManagementApplier(logger);

logger.WriteLineInfo("// Found benchmarks:");
foreach (var benchmark in benchmarks)
logger.WriteLineInfo($"// {benchmark.DisplayInfo}");
logger.WriteLine();
foreach (var benchmark in benchmarks)
{
powerManagementApplier.ApplyPerformancePlan(benchmark.Job.Environment.PowerPlanMode.PowerPlan);
var info = buildResults[benchmark];
var buildResult = info.buildResult;

Expand Down Expand Up @@ -217,6 +216,7 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,

var clockSpan = runChronometer.GetElapsed();

powerManagementApplier.ApplyUserPowerPlan();
return new Summary(title,
reports,
HostEnvironmentInfo.GetCurrent(),
Expand Down
37 changes: 25 additions & 12 deletions src/BenchmarkDotNet/Running/PowerManagementApplier.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
Expand All @@ -11,26 +13,37 @@ internal class PowerManagementApplier
private Guid? userCurrentPowerPlan;
private bool powerPlanChanged = false;
private bool isInitialized = false;
private static readonly Dictionary<PowerPlan, string> powerPlansDict = new Dictionary<PowerPlan, string>()
{
{ PowerPlan.UserPowerPlan, null },
{ PowerPlan.HighPerformance, "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c" }
};

internal PowerManagementApplier(ILogger logger)
{
this.logger = logger;
}

internal void ApplyPerformancePlan(bool highPerformancePowerPlan)
internal void ApplyPerformancePlan(PowerPlan powerPlan)
{
var guid = powerPlansDict[powerPlan];
ApplyPerformancePlan(guid);
}

internal void ApplyPerformancePlan(string guid)
{
if (RuntimeInformation.IsWindows())
{
if (highPerformancePowerPlan && powerPlanChanged == false)
ApplyHighPerformancePlan(logger);
else if (highPerformancePowerPlan == false)
ApplyUserPowerPlan(logger);
if (string.IsNullOrEmpty(guid) == false && powerPlanChanged == false)
ApplyPlanByGuid(guid);
else if (string.IsNullOrEmpty(guid))
ApplyUserPowerPlan();
}
}

internal void ApplyUserPowerPlan(ILogger logger)
internal void ApplyUserPowerPlan()
{
if (powerPlanChanged && userCurrentPowerPlan != null && RuntimeInformation.IsWindows())
if (powerPlanChanged && RuntimeInformation.IsWindows())
{
try
{
Expand All @@ -48,7 +61,7 @@ internal void ApplyUserPowerPlan(ILogger logger)
}
}

private void ApplyHighPerformancePlan(ILogger logger)
private void ApplyPlanByGuid(string guid)
{
try
{
Expand All @@ -58,18 +71,18 @@ private void ApplyHighPerformancePlan(ILogger logger)
isInitialized = true;
}

if (PowerManagementHelper.Set(new Guid(PowerManagementHelper.HighPerformanceGuid)))
if (PowerManagementHelper.Set(new Guid(guid)))
{
powerPlanChanged = true;
var powerPlanFriendlyName = PowerManagementHelper.CurrentPlanFriendlyName;
logger.WriteInfo($"Setup power plan (GUID: {PowerManagementHelper.HighPerformanceGuid} FriendlyName: {powerPlanFriendlyName})");
logger.WriteInfo($"Setup power plan (GUID: {guid} FriendlyName: {powerPlanFriendlyName})");
}
else
logger.WriteLineError($"Cannot setup power plan (GUID: {PowerManagementHelper.HighPerformanceGuid})");
logger.WriteLineError($"Cannot setup power plan (GUID: {guid})");
}
catch (Exception ex)
{
logger.WriteLineError($"Cannot setup power plan (GUID: {PowerManagementHelper.HighPerformanceGuid}, error message: {ex.Message})");
logger.WriteLineError($"Cannot setup power plan (GUID: {guid}, error message: {ex.Message})");
}
}
}
Expand Down
Loading

0 comments on commit f3a51c2

Please sign in to comment.