Universe.CpuUsage
Universe.CpuUsage copied to clipboard
CPU Usage for .NET Core and .NET Framework for Linux, Windows and macOS with coverage of async/await scenarios.
Universe.CpuUsage 
CPU Usage for .NET Core, .NET Framework and Mono on Linux, Windows and macOS
Crossplatform CPU Usage metrics
It receives the amount of time that the current thread(s)/process has executed in kernel and user mode.
Works everywhere: Linux, OSX and Windows.
Targets everywhere: Net Framework 2.0+, Net Standard 1.3+, Net Core 1.0+
Coverage and supported OS
Minimum OS requirements: Linux Kernel 2.6.26, Mac OS 10.9, Windows XP/2003
Autotests using .NET Core and .NET Framework cover:
- Linux x86_64 and Arm 64-bit on appveyor
- Windows Server 2016 on appveyor
- macOS 10.14 on travis-ci.org
- Raspbian 10 Buster on Azure Piplines using self-hosted agent on Raspberry pi
Autotests using Mono:
- Linux x86_64, Arm 64-bit, Arm-v7 32 bit, i386 using mono on travis-ci.org
- Mac OSX 10.10 using travis-ci.org
Manually tested on:
- Windows 7 x86 (.NET Core), Windows 10 ARM64 (.NET Core)
- FreeBSD 12 (both .NET Core and Mono).
- Debian 8 on armv5 (Mono)
appveyor | travis-ci |
---|---|
.NET Core: Linux x64, Windows x64. Mono: Linux x64. |
.NET Core: macOS 10.14, Linux Arm 64. Mono: Linux Arm 64, Arm-v7, i386, macOS 10.10. |
Integration tests on exotic platforms
For mono only platforms (i386, ppc64, mips, arm v5/v6, etc) here is the script for integration tests: test-on-mono-only-platforms.sh
url=https://raw.githubusercontent.com/devizer/Universe.CpuUsage/master/test-on-mono-only-platforms.sh;
(wget -q -nv --no-check-certificate -O - $url 2>/dev/null || curl -ksSL $url) | bash
Implementation
The implementation utilizes platform invocation (P/Invoke) of the corresponding system libraries depending on the OS:
OS | per thread implementation | per process implementation | library |
---|---|---|---|
Linux | getrusage(RUSAGE_THREAD) | getrusage(RUSAGE_SELF) | libc.so |
Windows | GetThreadTimes() | GetProcessTimes() | kernel32.dll |
Mac OS X | thread_info() | getrusage(RUSAGE_SELF) | libSystem.dylib |
Precision depends on
Here is a summary of CPU usage precision. In general, it depends on OS and version and it does not depend on CPU performance except of FreeBSD
OS | Average Precision |
---|---|
Windows Server 2019, Xeon E5-2697 v3 | 16,250 μs |
Linux, Xeon E5-2697 v3 @ 2.60GHz, kernel 5.0 | 3,800 μs |
Linux, ARMv7 H3 CPU, 1.50GHz, kernel 3.4 | 10,000 μs |
Linux, ARMv7 H3 CPU, 1.10GHz, kernel 4.19 | 3,800 μs |
Mac OS 10.14, Xeon E5-2697 v2 @ 2.70GHz | 14.0 μs |
FreeBSD 12, .NET Core 2.0, Xeon E3-1270 v2 @ 3.50GHz, pseudo kernel 2.6.32 | 3.0 μs |
FreeBSD 12, Mono 5.1, Xeon E3-1270 v2 @ 3.50GHz, native BSD 12 | 1.9 μs |
Detailed histograms of precision are produced by PrecisionTest.cs
Low level API: class CpuUsage
var onStart = CpuUsage.GetByThread();
...
var onEnd = CpuUsage.GetByThread();
Console.WriteLine("CPU Usage: " + (onEnd-onStart));
High level API: class CpuUsageAsyncWatcher
public void Configure(IApplicationBuilder app)
{
// Here is a "middleware" that displays total CPU usage
// of all the Tasks executed by ASP.NET Core pipeline during each http request
app.Use(async (context, next) =>
{
CpuUsageAsyncWatcher watcher = new CpuUsageAsyncWatcher();
await next.Invoke();
watcher.Stop();
Console.WriteLine($"Cpu Usage by http request is {watcher.GetSummaryCpuUsage()}");
});
}
Benchmark
Just for illustration here is a comparison to well known DateTime.Now and Stopwatch using benchmark.net. All of them are taken using .NET Core 3.0 runtime.
Linux x64, kernel 4.15
Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
DateTime.Now() | 150.24 ns | 2.346 ns | 2.194 ns | 2 | - | - | - | - |
Stopwatch | 77.27 ns | 0.473 ns | 0.419 ns | 1 | 0.0095 | - | - | 40 B |
Process CPU Usage | 795.18 ns | 8.000 ns | 6.681 ns | 3 | - | - | - | - |
Thread CPU Usage | 834.59 ns | 9.324 ns | 8.722 ns | 4 | - | - | - | - |
Linux 32 bit @ ARM, kernel 3.4 (Orange PI, H3, 1500 MHz)
Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
DateTime.Now | 2.788 μs | 0.0595 μs | 0.0944 μs | 2 | - | - | - | - |
Stopwatch | 1.737 μs | 0.0539 μs | 0.0504 μs | 1 | 0.1945 | - | - | 32 B |
Process CPU Usage | 5.552 μs | 0.1662 μs | 0.4900 μs | 3 | - | - | - | - |
Thread CPU Usage | 5.664 μs | 0.1136 μs | 0.1960 μs | 3 | - | - | - | - |
macOS 10.14
Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
DateTime.Now() | 73.40 ns | 1.506 ns | 2.475 ns | 1 | - | - | - | - |
Stopwatch | 79.36 ns | 1.640 ns | 1.454 ns | 2 | 0.0025 | - | - | 40 B |
Process CPU Usage | 1,979.10 ns | 29.771 ns | 26.391 ns | 4 | - | - | - | - |
Thread CPU Usage | 1,921.54 ns | 35.869 ns | 33.552 ns | 3 | - | - | - | - |
Windows Server 2016
Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
DateTime.Now() | 217.08 ns | 1.667 ns | 1.559 ns | 4 | - | - | - | - |
Stopwatch | 31.12 ns | 0.203 ns | 0.169 ns | 1 | 0.0095 | - | - | 40 B |
Process CPU Usage | 200.49 ns | 3.743 ns | 3.501 ns | 2 | - | - | - | - |
Thread CPU Usage | 205.11 ns | 3.970 ns | 4.413 ns | 3 | - | - | - | - |
Legend
- Stopwatch:
var sw = new Stopwatch(); var ticks = sw.ElapsedTicks;
- Process CPU Usage:
CpuUsageReader.GetByProcess();
- Thread CPU Usage:
CpuUsageReader.GetByThread();
- ns - nanosecond, μs - microsecond