起因

再看.Net 官方博客 <<.Net 5性能优化>> 中,发现测试性能的Benchmarkdotnet版本已经是v0.12.1,然后去看BenchmarkDotNet文档,发现还是有不少新的特性.

v0.12.0

  1. 支持多个运行时(API改进),增加对.Net 5支持
  2. 支持DotNet创建BenchmarkDotNet项目(项目模版)
  3. 增加NativeMemoryProfiler(目前仅支持Windows,需要在Nuget管理器中安装BenchmarkDotNet.Diagnostics.Windows包,才可以,内部使用EtwProfiler)
  4. 增加ThreadingDiagnoser
  5. 增加MemoryDiagnoser
  6. LINQPad 6进行支持,可以在LINQPad 6进行代码性能测试(LINQPad 要收费版才可以,这里也跳过)
  7. 文档快速搜索

v0.12.1

  1. 跨平台生成汇编代码
  2. 基于事件管道跨平台Profiler
  3. 新的API,使用更方便
支持多个运行时,新增.Net 5

<!--新增.Net 5运行时--> <TargetFrameworks>net5.0;netcoreapp3.1;net48</TargetFrameworks>

//以.NetFramework 4.8为基准,测试三个版本 .NetFramework 4.8/.Net Core 3.1和.Net 5 dotnet run -c Release -f net48 --runtimes net48 netcoreapp31 netcoreapp50 --filter ** --join

BenchmarkDotNet项目模板

先查看.Net 5下,有什么项目模板(不是必需,可以自己新建项目引用BenchmarkDotNet):

//查看.Net有哪些项目模板 dotnet new -l

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(1)

.Net 5自带的项目模版

通过命令安装模板:

// -i 代表install dotnet new -i BenchmarkDotNet.Templates

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(2)

在.Net 5中安装BenchmarkDotNet的项目模板

通过命令卸载安装过的模板:

//-u 代表卸载 u为uninsall dotnet new -u BenchmarkDotNet.Templates

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(3)

在.Net 5中卸载BenchmarkDotNet项目模板

新建Benchmark项目:

//新建BenchmarkDotNet项目 dotnet new benchmark --console-app

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(4)

在.Net 5中通过BenchmarkDotNet项目模板新建项目Benchmark项目

NativeMemoryProfiler使用

在Nuget管理器中安装BenchmarkDotNet.Diagnostics.Windows包.

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(5)

在Nuget管理器中安装BenchmarkDotNet.Diagnostics.Windows

执行后生成的结果(没有执行完成,是因为电脑在运行的时候突然蓝屏,怀疑是CPU温度过高造成的,因为笔记本好几年没有换过散热硅脂了):

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(6)

BenchmarkDotNet 执行生成的结果

看BenchmarkDotNet文档中代码:

using System; using System.Drawing; using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Diagnostics.Windows.Configs; namespace dotnet_perf { [DisassemblyDiagnoser] [NativeMemoryProfiler] //在BenchmarkDotNet.Diagnostics.Windows.Configs命名空间下 [MemoryDiagnoser] public class IntroNativeMemory { [Benchmark] public void BitmapWithLeaks() { var flag = new Bitmap(200, 100); var graphics = Graphics.FromImage(flag); var blackPen = new Pen(Color.Black, 3); graphics.DrawLine(blackPen, 100, 100, 500, 100); } [Benchmark] public void Bitmap() { using (var flag = new Bitmap(200, 100)) { using (var graphics = Graphics.FromImage(flag)) { using (var blackPen = new Pen(Color.Black, 3)) { graphics.DrawLine(blackPen, 100, 100, 500, 100); } } } } private const int Size = 20; // Greater value could cause System.OutOfMemoryException for test with memory leaks. private int ArraySize = Size * Marshal.SizeOf(typeof(int)); [Benchmark] public unsafe void AllocHGlobal() { IntPtr unmanagedHandle = Marshal.AllocHGlobal(ArraySize); Span<byte> unmanaged = new Span<byte>(unmanagedHandle.ToPointer(), ArraySize); Marshal.FreeHGlobal(unmanagedHandle); } [Benchmark] public unsafe void AllocHGlobalWithLeaks() { IntPtr unmanagedHandle = Marshal.AllocHGlobal(ArraySize); Span<byte> unmanaged = new Span<byte>(unmanagedHandle.ToPointer(), ArraySize); } } }

ThreadingDiagnoser使用

using System.Threading; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Diagnosers; namespace dotnet_perf { [ThreadingDiagnoser] //在BenchmarkDotNet.Diagnosers命名空间下 public class IntroThreadingDiagnoser { [Benchmark] public void CompleteOneWorkItem() { ManualResetEvent done = new ManualResetEvent(initialState: false); ThreadPool.QueueUserWorkItem(m => (m as ManualResetEvent).Set(), done); done.WaitOne(); } } }

执行结果(可以看到在.Net 5和.Net Core 3.1性能相差不大,是因为在.Net 5中并没有对ThreadPool进行改进,但对异步是有改进):

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(7)

ThreadPool.QueueUserWorkItem在.Net 5和.Net Core 3.1性能对比,没有改进

MemoryDiagnoser使用

MemoryDiagnoser这个使用较多,当时写博客的时候,笔记本散热不太好,这一块跳过去了.

[MemoryDiagnoser] //使用MemoryDiagnoser特性标记 public class StringCreateTest { [Benchmark(Baseline = true)] public void ArrayTest() { //省略测试代码 } }

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(8)

BenchmarkDotNet 显示测试方法所分配内存大小

v0.12.1 生成跨平台汇编代码

using System; using BenchmarkDotNet.Attributes; namespace dotnet_perf { [DisassemblyDiagnoser(printSource:true)] [RyuJitX64Job] public class TestJit { private B[] _array = new B[42]; [Benchmark] public int Ctor() => new Span<B>(_array).Length; } class A { } sealed class B : A { } }

//多版本测试 dotnet run -c Release -f net48 --runtimes net48 netcoreapp31 netcoreapp50 --filter ** --join

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(9)

BenchmarkDotNet 生成多个平台的汇编代码,支持Linux

BenchmarkDotNet 生成汇编代码,和原先不一样,原先是要到ObjDump.exe(是需要安装MingW/Cygwin),现在需要iced库(纯C#代码实现,另外有Rust实现).说起这个比较坑.BenchmarkDotNet v0.12.1 依赖的iced 1.4.0版本,使用新版本,是有异常的.iced库目前只支持X86架构(32位和64位),看代码中没有Arm相关的目录,应该是不支持的.

benchmark已停止工作(BenchmarkDotNetv0.12x新增功能)(10)

BenchmarkDotNet 生成汇编代码对比,发现.Net 5生成汇编代码更小,说明性能也更好

,