起因
在看.Net 官方博客 .Net 5性能优化 中,发现测试性能的BenchmarkDotNet版本已经是v0.12.1,然后去看BenchmarkDotNet文档,发现还是有不少新的特性.
v0.12.0
支持多个运行时(API改进),增加对.Net 5支持
支持DotNet创建BenchmarkDotNet项目(项目模版)
增加NativeMemoryProfiler(目前仅支持Windows,需要在Nuget管理器中安装BenchmarkDotNet.Diagnostics.Windows包,才可以,内部使用EtwProfiler)
增加ThreadingDiagnoser
增加MemoryDiagnoser
对LINQPad 6进行支持,可以在LINQPad 6进行代码性能测试(LINQPad 要收费版才可以,这里也跳过)
文档快速搜索
v0.12.1
跨平台生成汇编代码
基于事件管道跨平台Profiler
新的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下,有什么项目模版:
dotnet new -l
通过命令安装模版:
#-i 代表install
dotnet new -i BenchmarkDotNet.Templates
通过命令卸载安装过的模版:
#-u 代表卸载 u为uninsall
dotnet new -u BenchmarkDotNet.Templates
新建Benchmark项目:
#新建BenchmarkDotNet项目
dotnet new benchmark --console-app
NativeMemoryProfiler使用
在Nuget管理器中安装BenchmarkDotNet.Diagnostics.Windows包
执行后生成的结果(没有执行完成,是因为电脑在运行的时候突然蓝屏,怀疑是CPU温度过高造成的,因为笔记本好几年没有换过散热硅脂了):
看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进行改进,但对异步是有改进):
MemoryDiagnoser使用
这个在笔记本没法跑出结果.是NativeMemoryProfiler一样,笔记本散热达不到.在测试的时候回突然黑屏.这里直接跳过.
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
BenchmarkDotNet 生成汇编代码,和原先不一样,原先是要到ObjDump.exe(是需要安装MingW/Cygwin),现在需要iced(库,纯C#代码实现,另外有Rust实现).说起这个比较坑.BenchmarkDotNet v0.12.1 是依赖的iced 1.4.0版本,使用新版本,是有异常的.iced库目前只支持X86架构(32位和64位),看代码中没有Arm相关的目录,应该是不支持的.
来源:oschina
链接:https://my.oschina.net/u/4321566/blog/4725891