Skip to content

Instantly share code, notes, and snippets.

@MarkPflug
Created December 6, 2021 04:21
Show Gist options
  • Select an option

  • Save MarkPflug/55173728458020c6d335cc099c891c0b to your computer and use it in GitHub Desktop.

Select an option

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;
}
}
}
@trontronicent
Copy link
Copy Markdown

Hey Mark, i would like to include your diagnoser into a project (strictly internal use , no redistribution), and would like to ask if youre OK with that - cheers for the good work

@MarkPflug
Copy link
Copy Markdown
Author

@trontronicent Feel free. You might also add a comment to dotnet/BenchmarkDotNet#1666 in the hopes that an official implementation gets added. The limitation of my implementation is that the benchmark needs to be run "InProc", which is not the default behavior.

@trontronicent
Copy link
Copy Markdown

that suits me well, as i run 'unsupported' configs, I need InProc anyways ;)
Thx a lot, what credits shall I include on the codefile doc header ? If nothing custom desired, I just add the URL to this gist.

@JohannesDeml
Copy link
Copy Markdown

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
Copy Markdown
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
Copy Markdown

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
Copy Markdown

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