Skip to content

Instantly share code, notes, and snippets.

@MarkPflug
Created December 6, 2021 04:21
Show Gist options
  • Save MarkPflug/55173728458020c6d335cc099c891c0b to your computer and use it in GitHub Desktop.
Save MarkPflug/55173728458020c6d335cc099c891c0b to your computer and use it in GitHub Desktop.
BenchmarkDotNet CPU utilization
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Benchmarks
{
public class CpuDiagnoserAttribute : Attribute, IConfigSource
{
public IConfig Config { get; }
public CpuDiagnoserAttribute()
{
Config = ManualConfig.CreateEmpty().AddDiagnoser(new CpuDiagnoser());
}
}
public class CpuDiagnoser : IDiagnoser
{
Process proc;
public CpuDiagnoser()
{
this.proc = Process.GetCurrentProcess();
}
public IEnumerable<string> Ids => new[] { "CPU" };
public IEnumerable<IExporter> Exporters => Array.Empty<IExporter>();
public IEnumerable<IAnalyser> Analysers => Array.Empty<IAnalyser>();
public void DisplayResults(ILogger logger)
{
}
public RunMode GetRunMode(BenchmarkCase benchmarkCase)
{
return RunMode.NoOverhead;
}
long userStart, userEnd;
long privStart, privEnd;
public void Handle(HostSignal signal, DiagnoserActionParameters parameters)
{
if(signal == HostSignal.BeforeActualRun)
{
userStart = proc.UserProcessorTime.Ticks;
privStart = proc.PrivilegedProcessorTime.Ticks;
}
if(signal == HostSignal.AfterActualRun)
{
userEnd = proc.UserProcessorTime.Ticks;
privEnd = proc.PrivilegedProcessorTime.Ticks;
}
}
public IEnumerable<Metric> ProcessResults(DiagnoserResults results)
{
yield return new Metric(CpuUserMetricDescriptor.Instance, (userEnd - userStart) * 100d / results.TotalOperations);
yield return new Metric(CpuPrivilegedMetricDescriptor.Instance, (privEnd - privStart) * 100d / results.TotalOperations);
}
public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters)
{
yield break;
}
class CpuUserMetricDescriptor : IMetricDescriptor
{
internal static readonly IMetricDescriptor Instance = new CpuUserMetricDescriptor();
public string Id => "CPU User Time";
public string DisplayName => Id;
public string Legend => Id;
public string NumberFormat => "0.##";
public UnitType UnitType => UnitType.Time;
public string Unit => "ns";
public bool TheGreaterTheBetter => false;
public int PriorityInCategory => 1;
}
class CpuPrivilegedMetricDescriptor : IMetricDescriptor
{
internal static readonly IMetricDescriptor Instance = new CpuPrivilegedMetricDescriptor();
public string Id => "CPU Privileged Time";
public string DisplayName => Id;
public string Legend => Id;
public string NumberFormat => "0.##";
public UnitType UnitType => UnitType.Time;
public string Unit => "ns";
public bool TheGreaterTheBetter => false;
public int PriorityInCategory => 1;
}
}
}
@JohannesDeml
Copy link

Hey Mark,

sorry for the ultra late reply. Thanks a lot for the script. i tried adding it to one of my projects, but sadly I don't get meaningful results. I added the diagnoser like so: https://github.com/JohannesDeml/MicroBenchmarksDotNet/blob/test/cpu-diagnoser/MicroBenchmarks.Extensions/DefaultBenchmarkConfig.cs#L39

And here are example results I get:

Method TimeoutDuration Mean Error StdDev CPU User Time CPU Privileged Time
ThreadSpinWait 2 2.001 ms 0.0003 ms 0.0003 ms - -
ThreadSleep0 2 2.001 ms 0.0002 ms 0.0002 ms - -
ThreadSleep 2 15.653 ms 0.1952 ms 0.1825 ms - -
ThreadSleepEnhanced 2 2.967 ms 0.0228 ms 0.0213 ms - -
TaskDelay 2 15.803 ms 0.2703 ms 0.2257 ms - -
TimerWait 2 15.643 ms 0.1521 ms 0.1348 ms - -
AutoResetEvent 2 15.669 ms 0.1561 ms 0.1461 ms - -
ThreadSpinWait 5 5.001 ms 0.0011 ms 0.0009 ms - -
ThreadSleep0 5 5.001 ms 0.0002 ms 0.0002 ms - -
ThreadSleep 5 15.645 ms 0.1543 ms 0.1443 ms - -
ThreadSleepEnhanced 5 5.956 ms 0.0278 ms 0.0260 ms - -
TaskDelay 5 15.720 ms 0.1555 ms 0.1378 ms - -
TimerWait 5 15.646 ms 0.2161 ms 0.1916 ms - -
AutoResetEvent 5 15.694 ms 0.2464 ms 0.2305 ms - 29592.8
ThreadSpinWait 20 20.001 ms 0.0003 ms 0.0002 ms - -
ThreadSleep0 20 20.001 ms 0.0005 ms 0.0005 ms - -
ThreadSleep 20 31.509 ms 0.1074 ms 0.1004 ms - -
ThreadSleepEnhanced 20 20.892 ms 0.0599 ms 0.0560 ms - -
TaskDelay 20 31.492 ms 0.2259 ms 0.2003 ms - -
TimerWait 20 31.582 ms 0.3439 ms 0.2871 ms - -
AutoResetEvent 20 31.572 ms 0.1158 ms 0.1083 ms - 29592.8

Something seems to be not working here. Do you have an idea what the problem is?

@MarkPflug
Copy link
Author

No idea, sorry. Seems that something in .net 7 broke it. I noticed this too and stopped using it for my benchmarks.

@JohannesDeml
Copy link

Thanks for the swift response! Okay, got it, very strange. And fyi, this seems to also affect .NET 6, so maybe it is more because of a change in how benchmarkdotnet spins up the processes or something like that? 🤷

@rpascalsdl
Copy link

Got it working under .NET 8:

  • Add [InProcess] on the class with the bechmarks.
  • You might have to run it with Administrator - sorry, too lazy to restart and check if it works without.
  • Start Without Debugger to get best results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment