BenchmarkDotNet v0.12.1
Highlights
- .NET 5 support
As you probably know, .NET Core 5 was officially rebranded to .NET 5. The new version of BenchmarkDotNet supports the new runtime after rebranding.
#1399 465ebf - Perfolizer adoption
The internal statistical engine of BenchmarkDotNet became mature enough to be transformed into an independent project. Meet perfolizer — a toolkit for performance analysis! While BenchmarkDotNet focuses on obtaining reliable measurements, perfolizer focuses on the decent analysis of measured data. You still can use all the statistical algorithms from BenchmarkDotNet, but you can also install perfolizer as a standalone NuGet package. You can find more details in the official announcement.
#1386 54a061 - Cross-platform disassembler
Now theDisassemblyDiagnoser
is cross-platform! The disassembling logic was also improved, now it handles runtime helper methods and references to method tables properly. Internally, it uses the Iced library for formatting assembly code.
Special thanks to @adamsitnik for the implementation and @0xd4d for Iced!
#1332 #899 #1316 #1364 294320 - EventPipe-based cross-platform profiler
Now you can easily profiler your benchmarks on Windows, Linux, and macOS! Just mark your class with the[EventPipeProfiler(...)]
attribute and get a.speedscope.json
file that you can browse in SpeedScope.
Special thanks to @WojciechNagorski for the implementation!
#1321 #1315 c648ff - New fluent API
We continue to improve our API and make it easier for reading and writing.
Special thanks to @WojciechNagorski for the implementation!
#1273 #1234 640d88 - Ref readonly support
Now you can useref readonly
in benchmark signatures.
Special thanks to @adamsitnik for the implementation!
#1389 #1388 9ac777
Cross-platform disassembler
Just mark your benchmark class with the [DisassemblyDiagnoser]
attribute
and you will get the disassembly listings for all the benchmarks.
The formatting looks pretty nice thanks to Iced.
It works like a charm on Windows, Linux, and macOS.
[DisassemblyDiagnoser]
public class IntroDisassembly
{
private int[] field = Enumerable.Range(0, 100).ToArray();
[Benchmark]
public int SumLocal()
{
var local = field; // we use local variable that points to the field
int sum = 0;
for (int i = 0; i < local.Length; i++)
sum += local[i];
return sum;
}
[Benchmark]
public int SumField()
{
int sum = 0;
for (int i = 0; i < field.Length; i++)
sum += field[i];
return sum;
}
}
.NET Core 2.1.16 (CoreCLR 4.6.28516.03, CoreFX 4.6.28516.10), X64 RyuJIT
; BenchmarkDotNet.Samples.IntroDisassembly.SumLocal()
mov rax,[rcx+8]
xor edx,edx
xor ecx,ecx
mov r8d,[rax+8]
test r8d,r8d
jle short M00_L01
M00_L00:
movsxd r9,ecx
add edx,[rax+r9*4+10]
inc ecx
cmp r8d,ecx
jg short M00_L00
M00_L01:
mov eax,edx
ret
; Total bytes of code 35
.NET Core 2.1.16 (CoreCLR 4.6.28516.03, CoreFX 4.6.28516.10), X64 RyuJIT
; BenchmarkDotNet.Samples.IntroDisassembly.SumField()
sub rsp,28
xor eax,eax
xor edx,edx
mov rcx,[rcx+8]
cmp dword ptr [rcx+8],0
jle short M00_L01
M00_L00:
mov r8,rcx
cmp edx,[r8+8]
jae short M00_L02
movsxd r9,edx
add eax,[r8+r9*4+10]
inc edx
cmp [rcx+8],edx
jg short M00_L00
M00_L01:
add rsp,28
ret
M00_L02:
call CORINFO_HELP_RNGCHKFAIL
int 3
; Total bytes of code 53
Now we handle runtime helper methods and references to method tables properly. Example:
Before:
; MicroBenchmarks.WithCallsAfter.Benchmark(Int32)
push rsi
sub rsp,20h
mov rsi,rcx
cmp edx,7FFFFFFFh
jne M00_L00
call MicroBenchmarks.WithCallsAfter.Static()
mov rcx,rsi
call MicroBenchmarks.WithCallsAfter.Instance()
mov rcx,rsi
call MicroBenchmarks.WithCallsAfter.Recursive()
mov rcx,rsi
mov rax,qword ptr [rsi]
mov rax,qword ptr [rax+40h]
call qword ptr [rax+20h]
mov rcx,rsi
mov edx,1
mov rax,7FF8F4217050h
add rsp,20h
pop rsi
jmp rax
M00_L00:
mov rcx,offset System_Private_CoreLib+0xa31d48
call coreclr!MetaDataGetDispenser+0x322a0
mov rsi,rax
mov ecx,0ACFAh
mov rdx,7FF8F42F4680h
call coreclr!MetaDataGetDispenser+0x17140
mov rdx,rax
mov rcx,rsi
call System.InvalidOperationException..ctor(System.String)
mov rcx,rsi
call coreclr!coreclr_shutdown_2+0x39f0
int 3
add byte ptr [rax],al
sbb dword ptr [00007ff9`26284e30],eax
add dword ptr [rax+40h],esp
add byte ptr [rax],al
add byte ptr [rax],al
add byte ptr [rax],al
add byte ptr [rax-70BC4CCh],ah
; Total bytes of code 157
After:
; BenchmarkDotNet.Samples.WithCallsAfter.Benchmark(Int32)
push rsi
sub rsp,20
mov rsi,rcx
cmp edx,7FFFFFFF
jne M00_L00
call BenchmarkDotNet.Samples.WithCallsAfter.Static()
mov rcx,rsi
call BenchmarkDotNet.Samples.WithCallsAfter.Instance()
mov rcx,rsi
call BenchmarkDotNet.Samples.WithCallsAfter.Recursive()
mov rcx,rsi
mov rax,[rsi]
mov rax,[rax+40]
call qword ptr [rax+20]
mov rcx,rsi
mov edx,1
mov rax BenchmarkDotNet.Samples.WithCallsAfter.Benchmark(Boolean)
add rsp,20
pop rsi
jmp rax
M00_L00:
mov rcx MT_System.InvalidOperationException
call CORINFO_HELP_NEWSFAST
mov rsi,rax
mov ecx,12D
mov rdx,7FF954FF83F0
call CORINFO_HELP_STRCNS
mov rdx,rax
mov rcx,rsi
call System.InvalidOperationException..ctor(System.String)
mov rcx,rsi
call CORINFO_HELP_THROW
int 3
; Total bytes of code 134
See also: Cross-runtime .NET disassembly with BenchmarkDotNet.
Special thanks to @adamsitnik for the implementation and @0xd4d for Iced!
EventPipe-based cross-platform profiler
Now you can easily profiler your benchmarks on Windows, Linux, and macOS!
If you want to use the new profiler, you should just mark your benchmark class with the [EventPipeProfiler(...)]
attribute:
[EventPipeProfiler(EventPipeProfile.CpuSampling)] // <-- Enables new profiler
public class IntroEventPipeProfiler
{
[Benchmark]
public void Sleep() => Thread.Sleep(2000);
}
Once the benchmark run is finished, you get a .speedscope.json
file that can be opened in SpeedScope:
The new profiler supports several modes:
CpuSampling
- Useful for tracking CPU usage and general .NET runtime information. This is the default option.GcVerbose
- Tracks GC collections and samples object allocations.GcCollect
- Tracks GC collections only at very low overhead.Jit
- Logging when Just in time (JIT) compilation occurs. Logging of the internal workings of the Just In Time compiler. This is fairly verbose. It details decisions about interesting optimization (like inlining and tail call)
Please see Wojciech Nagórski's blog post for all the details.
Special thanks to @WojciechNagorski for the implementation!
New fluent API
We continue to improve our API and make it easier for reading and writing. The old API is still existing, but it is marked as obsolete and will be removed in the further library versions. The most significant changes:
Changes in Job configuration
Changes in IConfig/ManualConfig
Full fluent API
Special thanks to @WojciechNagorski for the implementation!
Ref readonly support
Now you can use ref readonly
in benchmark signatures.
Here is an example:
public class RefReadonlyBenchmark
{
static readonly int[] array = { 1 };
[Benchmark]
public ref readonly int RefReadonly() => ref RefReadonlyMethod();
static ref readonly int RefReadonlyMethod() => ref array[0];
}
Special thanks to @adamsitnik for the implementation!
Milestone details
In the v0.12.1 scope, 31 issues were resolved and 42 pull requests were merged. This release includes 85 commits by 19 contributors.
Resolved issues (31)
- #641 RPlotExporter hanging (assignee: @m-mccormick)
- #899 Tiered compilation and disassembler (assignee: @adamsitnik)
- #1023 Out of process benchmarks fail with ASP.NET Core SDK reference
- #1211 Binding Redirect Issues When Using Xml Serializers
- #1234 Strong type fluent API proposal (assignee: @WojciechNagorski)
- #1238 RunAllJoined Causing Exception (assignee: @gsomix)
- #1262 Params attribute doesn`t work in F# if you specify more than one enum value in constructor (assignee: @gsomix)
- #1295 Custom format/culture for report output values (for CSV, and maybe HTML, MD)
- #1305 Copy UserSecrets from benchmark project
- #1311 Spelling nit (assignee: @AndreyAkinshin)
- #1312 Add an option to pass environment variables to the default job
- #1315 Implement cross platform EventPipeProfiler diagnoser (assignee: @WojciechNagorski)
- #1316 Implement Unix Disassembler for .NET Core (assignee: @adamsitnik)
- #1318 use of NugetReference[] causes System.MissingMethodException: No parameterless constructor defined for this object. (assignee: @adamsitnik)
- #1323 DisassemblyDiagnoser index outside array bounds (assignee: @AndreyAkinshin)
- #1325 Surface native code size benchmarked code (assignee: @adamsitnik)
- #1326 BDN does not build using dotnet sdk from the command line in Linux
- #1339 Generated code and StyleCop.Analyzers (assignee: @adamsitnik)
- #1348 Different display text for arrays depending on a value source (assignee: @YohDeadfall)
- #1350 Warn the user if command line arguments were not passed to the BenchmarkSwitcher
- #1353 Show Length when param type is an array
- #1361 SimpleJobAttribute with RunStrategy and RuntimeMoniker
- #1363 Wrong assembly binding redirects for Microsoft.Data.SqlClient.resources ; using in Netcore3.0 project (assignee: @adamsitnik)
- #1364 Bug: Benchmark class with Console.WriteLine(1) fails for DisassemblyDiagnoser with 'Sequence contains no matching element' (assignee: @adamsitnik)
- #1369 Parameter column doesn't seem to respect culture info (assignee: @Tyrrrz)
- #1379 Unix CI builds are red (assignee: @AndreyAkinshin)
- #1385 Make BaselineCustomColumn.GetValue public
- #1388 'ref readonly' return is not supported (assignee: @adamsitnik)
- #1396 MacOS Azure Pipeline build is broken (assignee: @AndreyAkinshin)
- #1413 Plot with only one "default" Job (assignee: @AndreyAkinshin)
- #1416 EventPipeProfiler Documentation (assignee: @WojciechNagorski)
Merged pull requests (42)
- #1258 Task add style cope and fxcop analyzers (by @WojciechNagorski)
- #1263 Configuration compatibility validation (by @gsomix)
- #1266 Add EnumParam preserving type information (by @gsomix)
- #1273 New fluent API (by @WojciechNagorski)
- #1287 EdPeltChangePointDetector improvements (by @jeanbern)
- #1300 Update link of " official benchmarking guide" to use the new recommended link (by @eriawan)
- #1301 Improve BenchmarkDotNet.Templates (by @AndreyAkinshin)
- #1302 CultureInfo Refactoring (by @AndreyAkinshin)
- #1307 Fix project file order (by @vilinski)
- #1309 Copy UserSecrets from benchmark project (by @kant2002)
- #1313 add possibility to specify env vars via console line arguments (by @adamsitnik)
- #1321 The EventPipeProfiler cross-platform profiler (by @WojciechNagorski)
- #1327 Add package-refs to reference assemblies for linux build (by @damageboy)
- #1329 Show information about docker (by @WojciechNagorski)
- #1331 Use 24-hour time in joined summary file name (by @jroessel)
- #1332 Improved and Cross platform disassembler (by @adamsitnik)
- #1335 Attribute improvements (by @WojciechNagorski)
- #1340 Improvement of csproj finding (by @WojciechNagorski)
- #1349 Fixed display text for array parameters and arguments (by @YohDeadfall)
- #1357 add the
header at the top of the generated file to avoid static analysis tools from analyzing it, (by @adamsitnik) - #1359 Warn if command line arguments were not passed to the BenchmarkSwitcher (by @suslovk)
- #1365 Use DirtyAssemblyResolveHelper only for Full .NET Framework (by @adamsitnik)
- #1366 add missing SimpleJobAttribute ctor (by @adamsitnik)
- #1367 Await non-generic ValueTask returning method (by @mayuki)
- #1372 Use CultureInfo when formatting parameter values (by @Tyrrrz)
- #1373 fixes #641 (by @m-mccormick)
- #1375 don't require the JitDiagnosers (TailCall & Inlining) to run benchmarks once again just to gather JIT info, the overhead is very small (by @adamsitnik)
- #1376 update TraceEvent to 2.0.49 to get TailCalls working again (by @adamsitnik)
- #1380 Bump Cake version from 0.30.0 to 0.37.0, fix #1379 (by @AndreyAkinshin)
- #1381 Minor event pipe profiler improvements (by @adamsitnik)
- #1384 Fix build after styleCop (by @WojciechNagorski)
- #1386 Switch to perfolizer (by @AndreyAkinshin)
- #1387 Make BaselineCustomColumn expose "GetValue" as a public API (by @damageboy)
- #1389 Ref readonly support (by @adamsitnik)
- #1394 Align homepage example with README (by @dahlbyk)
- #1397 Bump macOS Azure Pipeline vmImage to 10.14, fix #1396 (by @AndreyAkinshin)
- #1399 React to .NET 5 branding changes (by @jkotas)
- #1407 Improve warnings for small operations number (by @CodeFuller)
- #1410 Updating Document - Fixing a small grammar mistake (by @abhinavgalodha)
- #1417 Fix --profiler option description. (by @WojciechNagorski)
- #1418 EventPipeProfiler documentation (by @WojciechNagorski)
- #1419 Small fix in EventPipeProfiler documentation (by @WojciechNagorski)
Commits (85)
- 396d4b #1262: Add EnumParam preserving type information (by @gsomix)
- 718b77 #1262: Add tests (by @gsomix)
- 33ec90 EdPeltChangePointDetector improvements (by @jeanbern)
- 7e3efc Postrelease update of v0.12.0 changelog (by @AndreyAkinshin)
- 8677b0 Update css in documentation (by @AndreyAkinshin)
- 919b7f Fix link to IntroThreadingDiagnoser in diagnoser.md (by @AndreyAkinshin)
- 4d15ea Update README.md (by @AndreyAkinshin)
- f2d081 Update link of " official benchmarking guide" to use the new recommended link... (by @eriawan)
- 05df0e Documentation: update article about command-line tool (by @AndreyAkinshin)
- d69505 Improve BenchmarkDotNet.Templates (#1301) (by @AndreyAkinshin)
- e23755 Speed up some integration tests (by @AndreyAkinshin)
- 3d96bf Update NuGet package descriptions (by @AndreyAkinshin)
- 351dae Update README.md (by @AndreyAkinshin)
- 5f07f4 Add Windows 10 brand string for 19H1 (by @AndreyAkinshin)
- fd92ff Update README.md (by @AndreyAkinshin)
- 5af5c5 Update README.md (by @AndreyAkinshin)
- 1b923f Fix project file order (#1307) (by @vilinski)
- 57b01f Copy UserSecrets from benchmark project (#1309) (by @kant2002)
- 2415fd Fix some typos, fix #1311 (by @AndreyAkinshin)
- e92d6d add possibility to specify env vars via console line arguments (#1313) (by @adamsitnik)
- b7054c WithNuGet should accept NuGetReferenceList, not IReadOnlyCollection<NuGetRefe... (by @adamsitnik)
- bb437b Remove curly braces for single statements (by @AndreyAkinshin)
- 76c675 Improve code readability (by @AndreyAkinshin)
- 8486e1 Merge pull request #1287 from jeanbern/patch-1 (by @AndreyAkinshin)
- d6bf40 Update README.md (by @AndreyAkinshin)
- 728c40 Support empty lines in disassembler/GetSmartPrefix, fix #1323 (by @AndreyAkinshin)
- 0c48c2 CultureInfo Refactoring (#1302) (by @AndreyAkinshin)
- 099550 Switch to using ms ref aseemblies for build (#1327) (by @damageboy)
- 55842b Merge pull request #1266 from gsomix/feature/1262-fsharp-enums (by @AndreyAkinshin)
- 35d358 Show information about docker (#1329) (by @WojciechNagorski)
- b6283c Use 24-hour time in joined summary file name (#1331) (by @jroessel)
- 640d88 New fluent API (#1273), fixes #1234 (by @WojciechNagorski)
- a3f76b Update README (by @AndreyAkinshin)
- 4a9697 Update copyright year (by @AndreyAkinshin)
- 5e7f01 add the
header at the top of the generated file to avoid s... (by @adamsitnik) - 07b512 Await non-generic ValueTask returning method (#1367) (by @mayuki)
- 479177 Display array length for array parameters and arguments (#1349), fixes #1348 ... (by @YohDeadfall)
- 3b8d2c Warn if command line arguments were not passed to the BenchmarkSwitcher (#135... (by @suslovk)
- 87d85a Use CultureInfo when formatting parameter values (#1372) (by @Tyrrrz)
- 310b5a add missing SimpleJobAttribute ctor, fixes #1361 (#1366) (by @adamsitnik)
- 6a1458 change the way RPlotExporter reads the R script output, fixes #641 (#1373) (by @m-mccormick)
- ff4c3d Attribute improvements (#1335) (by @WojciechNagorski)
- 29eafb Improvement of csproj finding (#1340) (by @WojciechNagorski)
- be2168 Use DirtyAssemblyResolveHelper only for Full .NET Framework (#1365) (by @adamsitnik)
- 664ab6 update TraceEvent to 2.0.49 to get TailCalls working again (#1376) (by @adamsitnik)
- b788bc don't require the JitDiagnosers (TailCall & Inlining) to run benchmarks once ... (by @adamsitnik)
- 8ad2a9 Bump Cake version from 0.30.0 to 0.37.0, fix #1379 (#1380) (by @AndreyAkinshin)
- 474047 Task add style cope and fxcop analyzers (#1258) (by @WojciechNagorski)
- c648ff The EventPipeProfiler cross-platform profiler (#1321) (by @WojciechNagorski)
- 19169a Add brand strings for latest Windows versions (by @AndreyAkinshin)
- 532f84 Update README (by @AndreyAkinshin)
- 3defd7 Minor event pipe profiler improvements (#1381) (by @adamsitnik)
- 294320 Improved and Cross platform disassembler (#1332) (by @adamsitnik)
- 1d63d6 Fix build after styleCop (#1384) (by @WojciechNagorski)
- b3ba08 Make BaselineCustomColumn expose "GetValue" as a public API (#1387) (by @damageboy)
- 54a061 Switch to perfolizer (by @AndreyAkinshin)
- 9ac777 Ref readonly support (#1389) (by @adamsitnik)
- c3286f Update CodeAnnotations.cs (by @AndreyAkinshin)
- 3223c9 Code cleanup (by @AndreyAkinshin)
- c19e54 Implement configurations compatibility validation (#1263), Closes #1238 (by @gsomix)
- df434e Align homepage example with README (#1394) (by @dahlbyk)
- 9a251a Bump macOS Azure Pipeline vmImage to 10.14, fix #1396 (#1397) (by @AndreyAkinshin)
- 465ebf React to .NET 5 branding changes (#1399) (by @jkotas)
- 6cca72 Updating a small grammar mistake (#1410) (by @abhinavgalodha)
- 27b32e Improve warnings for small operations number (#1407) (by @CodeFuller)
- 2d365b Fix --profiler option description. (#1417) (by @WojciechNagorski)
- 7902cd Add macOS Catalina support in OsBrandStringHelper (by @AndreyAkinshin)
- fd4c32 Update README (by @AndreyAkinshin)
- 6e13ba Resolving JobId in DefaultCharacteristicPresenter, fix #1413 (by @AndreyAkinshin)
- 35ebd2 Better job id generation in SimpleJobAttribute (by @AndreyAkinshin)
- bf4778 Use ASCII mode for Measurement presentation in terminal (by @AndreyAkinshin)
- f6b81f Display result path in RPlotExporter (by @AndreyAkinshin)
- 30b269 Disable plot printing in BuildPlots.R (by @AndreyAkinshin)
- 27887d Disable Rplots.pdf generation in BuildPlots.R (by @AndreyAkinshin)
- 338e40 Specify uid for how-to-run.md (by @AndreyAkinshin)
- f6dcd3 Add BenchmarkDotNet.Annotations in API documentation (by @AndreyAkinshin)
- b503fd Bump DocFX version from 2.46 to 2.51 (by @AndreyAkinshin)
- f457e7 Prepare v0.12.1 changelog (by @AndreyAkinshin)
- 53d090 Fix documentation.md (by @AndreyAkinshin)
- e72897 Add v0.12.1 highlights (by @AndreyAkinshin)
- 2de040 Bump perfolizer version from 0.2.0 to 0.2.1 (by @AndreyAkinshin)
- 76a070 EventPipeProfiler documentation (#1418) (by @WojciechNagorski)
- 928fb1 EventPipeProfiler doc improvements (#1419) (by @WojciechNagorski)
- 12798e Update documentation (by @AndreyAkinshin)
- 384d47 Set library version: 0.12.1 (by @AndreyAkinshin)
Contributors (19)
- Abhinav Galodha (@abhinavgalodha)
- Adam Sitnik (@adamsitnik)
- Andreas Vilinski (@vilinski)
- Andrey Akinshin (@AndreyAkinshin)
- Andrii Kurdiumov (@kant2002)
- CodeFuller (@CodeFuller)
- damageboy (@damageboy)
- Eriawan Kusumawardhono (@eriawan)
- Evgeniy Andreev (@gsomix)
- Jan Kotas (@jkotas)
- Jean-Bernard Pellerin (@jeanbern)
- Johannes Rössel [yWorks] (@jroessel)
- Keith Dahlby (@dahlbyk)
- Konstantin (@suslovk)
- Matt McCormick (@m-mccormick)
- Mayuki Sawatari (@mayuki)
- Oleksii Holub (@Tyrrrz)
- Wojciech Nagórski (@WojciechNagorski)
- Yoh Deadfall (@YohDeadfall)
Thank you very much!
Additional details
Date: April 6, 2020
Milestone: v0.12.1 (List of commits)
NuGet Packages:
- https://www.nuget.org/packages/BenchmarkDotNet/0.12.1
- https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.Windows/0.12.1
- https://www.nuget.org/packages/BenchmarkDotNet.Tool/0.12.1
- https://www.nuget.org/packages/BenchmarkDotNet.Annotations/0.12.1
- https://www.nuget.org/packages/BenchmarkDotNet.Templates/0.12.1