性能测试开发工具【免费下载链接】BenchmarkDotNetPowerful .NET library for benchmarking项目地址https://gitcode.com/gh_mirrors/be/BenchmarkDotNet点击查看免费下载在 BenchmarkDotNet 中默认的基准测试会为每个基准生成一个独立的控制台程序并在单独的进程中运行以实现进程级隔离。而 InProcess进程内工具链则完全绕开这一流程——它不生成任何新的可执行文件而是在宿主进程内部通过动态发射 IL 的方式直接运行基准适合追求极速启动、或需要在不被 BenchmarkDotNet 支持的运行时例如本地编译的 CoreCLR上运行基准的场景。读完本文你将掌握[InProcess]特性的用法、InProcessEmitToolchain与InProcessNoEmitToolchain的差异、配套的InProcessValidator校验规则以及如何在一个 Benchmark 类中同时对比进程内与进程外两种运行方式的实测差异。什么是 InProcess 工具链按照 toolchains.md 的定义为了获得进程级隔离BenchmarkDotNet 默认会为每一个基准生成、构建并执行一个新的控制台应用。一个toolchain工具链由 generator生成器、builder构建器和 executor执行器三部分组成未显式指定工具链时Full .NET Framework 与 Mono 默认使用 Roslyn 工具链.NET Core 与 NativeAOT 默认使用 dotnet cli 工具链而InProcess 工具链则完全不同它不生成新的可执行文件直接在调用方进程内完成基准运行。在 IntroInProcess.md 中官方对InProcessEmitToolchain给出了这样的定位InProcessEmitToolchain is our toolchain which does not generate any new executable. It emits IL on the fly and runs it from within the process itself.即在运行时动态发射 ILEmit IL on the fly并在当前进程内部执行。它的典型使用场景有两个追求极快的基准启动与运行速度——省去了每次运行都生成项目、调用编译器构建、再拉起子进程的开销在 BenchmarkDotNet 不支持的运行时上运行基准——例如你自己本地构建local build的 CoreCLR因为基准直接跑在宿主进程的运行时之上无需为它单独适配工具链。快速上手[InProcess]特性使用 InProcess 工具链最简洁的方式是在基准类上直接标注[InProcess]特性。官方文档给出的最小示例是[InProcess] public class TypeWithBenchmarks { }从源码看这个特性定义在 src/BenchmarkDotNet/Attributes/Jobs/InProcessAttribute.cs它是一个JobConfigBaseAttribute支持两个可选参数public InProcessAttribute( InProcessToolchainType toolchainType InProcessToolchainType.Auto, bool executeOnSeparateThread true)toolchainType取值为Auto、Emit、NoEmit三选一对应枚举InProcessToolchainType。当为Auto时特性会检测当前进程是否运行在 AOT 环境RuntimeInformation.IsAot若是则自动选择NoEmit否则选择Emit。原因在于 AOT 场景下没有动态 IL 发射能力只能退而使用基于反射的 NoEmit 实现executeOnSeparateThread默认true表示基准在独立的线程上执行对应底层InProcessEmitSettings.ExecuteOnSeparateThread设置见 src/BenchmarkDotNet/Toolchains/InProcess/InProcessSettings.cs。此外InProcessAttribute允许标注在类或程序集Assembly级别使用InProcessToolchainType.Auto时还会在未显式设置 Job Id 的情况下自动追加InProcess作为 Job 标识。完整示例剖析进程内 vs 进程外同台竞技官方示例 samples/BenchmarkDotNet.Samples/IntroInProcess.cs 展示了最有价值的用法在同一个 Benchmark 类里注册两个 Job一个走默认的进程外工具链一个走InProcessEmitToolchain从而直接对比两者差异。using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Order; using BenchmarkDotNet.Toolchains.InProcess.Emit; using System.Runtime.CompilerServices; namespace BenchmarkDotNet.Samples { [Config(typeof(Config))] [Orderer(SummaryOrderPolicy.FastestToSlowest)] [MemoryDiagnoser] public class IntroInProcess { private class Config : ManualConfig { public Config() { AddJob(Job.MediumRun .WithLaunchCount(1) .WithId(OutOfProc)); AddJob(Job.MediumRun .WithLaunchCount(1) .WithToolchain(InProcessEmitToolchain.Default) .WithId(InProcess)); } } [Benchmark(Description new byte[10kB])] public byte[] Allocate() { return new byte[10000]; } [Benchmark(Description stackalloc byte[10kB])] public unsafe void AllocateWithStackalloc() { var array stackalloc byte[10000]; Consume(array); } [MethodImpl(MethodImplOptions.NoInlining)] private static unsafe void Consume(byte* input) { } } }这段示例的关键点自定义 Config 配置两个 JobJob.MediumRun代表中等规模的运行计划默认包含一定数量的预热与测量迭代两个 Job 都通过.WithLaunchCount(1)把启动次数压到 1——这是为了在对比时尽量减少无关变量。第一个 Job 不指定工具链默认进程外第二个通过.WithToolchain(InProcessEmitToolchain.Default)切换到进程内InProcessEmitToolchain.Default这是该工具链的默认单例实例定义于 src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitToolchain.cs它绑定当前运行时RuntimeInformation.GetCurrentRuntime()与InProcessEmitSettings.Default。若需要自定义设置如ExecuteOnSeparateThread false可通过InProcessEmitToolchain.From(new InProcessEmitSettings { ... })创建新实例两个 Benchmark 都是典型的内存分配场景Allocate在托管堆上分配 10kB 字节数组AllocateWithStackalloc则在栈上分配同样大小并通过[MethodImpl(MethodImplOptions.NoInlining)]的Consume防止死代码消除——这样配合[MemoryDiagnoser]可以直观看到堆分配0 与 10000 bytes与栈分配的差异[Orderer(SummaryOrderPolicy.FastestToSlowest)]结果按耗时从快到慢排序方便直接比较 InProcess 与 OutOfProc 两个 Job 的表现。需要说明的是这个示例的核心目的并非证明进程内更快而是演示如何把进程内工具链接入既有基准配置体系。由于 InProcess 省去了进程启动与 JIT 预热等环节其测量结果与真实进程外运行存在差异请勿将两种 Job 的结果混为一谈用于绝对性能结论。进程内工具链的两种实现Emit 与 NoEmit在 src/BenchmarkDotNet/Toolchains/InProcess 目录下进程内工具链其实有两条实现路径工具链命名空间原理适用场景InProcessEmitToolchainBenchmarkDotNet.Toolchains.InProcess.Emit通过System.Reflection.Emit在内存中动态生成可运行类型的 IL 并直接执行常规 .NET 运行时默认选择InProcessNoEmitToolchainBenchmarkDotNet.Toolchains.InProcess.NoEmit不发射 IL直接基于反射MethodInfo.Invoke等驱动基准方法AOT 环境如 NativeAOT以及不支持动态代码生成的场景从 InProcessAttribute.cs 的源码可以看到Auto模式正是通过RuntimeInformation.IsAot在这两者之间自动抉择的。无论哪种实现都共享同一个基类设置InProcessSettings目前仅有ExecuteOnSeparateThread一个可配置项以及同一套InProcessValidator校验逻辑。底层执行机制执行器与运行器进程内工具链虽然不生成可执行文件但执行流程依然完整复刻了基准引擎的各个阶段GlobalSetup、预热、迭代测量、GlobalCleanup 等。其关键实现位于两个类1.InProcessEmitExecutor执行器见 src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitExecutor.cs当executeOnSeparateThread为true时会通过ExecutionContextHelper.SuppressFlow()压制执行上下文启动一个专用后台线程并配合BenchmarkSynchronizationContext确保基准在这个专用线程上运行而不是线程池线程若基准方法标记了[STAThread]且运行在 Windows 上该线程会被设置为 STA 公寓状态ApartmentState.STA这解决了部分依赖 STA 线程模型的基准在进程内运行时的兼容问题运行前后会临时将进程优先级提升为High、当前线程优先级提升为Highest并在 finally 中恢复原值若 Job 配置了Affinity还会设置进程 CPU 亲和性——这些都是为了尽量贴近进程外运行时的测量条件该工具链还支持在进程内路由 InProcess 诊断器CompositeInProcessDiagnoser相关逻辑使 MemoryDiagnoser 等诊断器在进程内模式下依然可用。2.InProcessEmitRunner运行器见 src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitRunner.cs首先调用host.BeforeAnythingElseAsync()让诊断器先行挂钩保证基于 JIT 的诊断器能捕获到第一次 JIT 编译从构建产物中取出动态生成的类型类型名前缀来自RunnableConstants.EmittedTypePrefix实例化后通过构造函数 out 参数拿到欺骗 JIT的委托trickTheJit再通过反射填充基准实例上的参数、[Params]参数以及标记了[BenchmarkCancellation]的CancellationToken字段/属性随后构造EngineParameters把 GlobalSetup/GlobalCleanup/IterationSetup/IterationCleanup 及 Workload 等回调全部绑定到动态类型的方法上交给标准的IEngine驱动完整的基准循环最后host.ReportResults(results)回报结果若基准抛出OutOfMemoryException运行器会给出专门的提示指出基准可能存在内存泄漏并建议使用OperationsPerInvoke、IterationSetup、IterationCleanup消除副作用这与进程外模式下由子进程自行处理异常的逻辑形成呼应。从源码结构看这一套设计让进程内模式尽可能复用了进程外模式的引擎与阶段逻辑差异主要被封装在如何构造可运行代码这一层Emit/NoEmit以及在哪里执行独立进程 vs 当前进程的专用线程这两处。环境一致性校验InProcessValidator进程内运行最大的风险是基准 Job 中声明的环境特征Platform、JIT、GC 模式等必须与当前宿主进程实际环境一致否则测量结果没有意义。为此进程内工具链配套提供了 src/BenchmarkDotNet/Toolchains/InProcess/InProcessValidator.csInProcessValidator.FailOnError将环境不匹配视为错误并终止运行InProcessValidator.DontFailOnError只报告警告不阻断运行。其校验规则表ValidationRules覆盖了以下特征特征类别具体特征校验策略环境Jit、Platform、GcMode.Server、GcMode.Concurrent、GcMode.CpuGroups、GcMode.NoAffinitize与实际环境比对不一致即报错/警告工具链Toolchain必须是InProcessEmitToolchain或InProcessNoEmitToolchain运行参数LaunchCount、RunStrategy、WarmupCount、IterationCount、IterationTime、InvocationCount、UnrollFactor等不校验进程内可自由设置精度参数MaxRelativeError、MaxAbsoluteError、OutlierMode、MinIterationTime等不校验同时校验器明确指出进程内工具链目前不支持带参数的基准方法Arguments——若基准方法带有参数会生成Arguments are not supported by the InProcessToolchain yet, see #687 for more details的校验错误。官方示例 samples/BenchmarkDotNet.Samples/IntroInProcessWrongEnv.cs 演示了故意制造环境不匹配的用法它通过Environment.Is64BitProcess反选平台64 位进程却声明 X86反之亦然构造一个与当前环境必然冲突的 Job并搭配AddValidator(InProcessValidator.DontFailOnError)让基准在警告模式下继续运行——这个示例常用于验证校验器确实在起作用以及理解进程内模式下声明的平台/架构实际不会被切换这一关键事实。注意进程内模式下声明了错误平台基准依然会跑但结果的合法性与代表性是存疑的正式使用时应采用FailOnError或保证配置与宿主进程环境完全一致。进程内模式的适用边界与注意事项综合官方文档与源码使用 InProcess 工具链前应明确以下边界适用场景需要快速验证基准逻辑正确性、追求极短的总运行时间、在自编译/自研运行时上运行基准、或作为集成测试的一部分在测试进程内直接执行基准仓库中大量集成测试如 tests/BenchmarkDotNet.IntegrationTests/InProcessEmitTest.cs、CallerThreadTests、ValuesReturnedByBenchmarkTest都依赖进程内模式来避免子进程编排的复杂度不适用/受限场景带参数的基准方法Arguments不受支持依赖进程级隔离的场景如测量进程启动开销、测试不同 JIT/运行时切换、需要独立内存空间的场合不应使用进程内模式环境一致性进程内模式下 JIT、Platform、GC 配置无法被真正切换必须通过InProcessValidator校验或人工保证 Job 配置与宿主进程一致结果解读进程内模式的迭代耗时与内存分配测量仍然有效但它缺少进程启动、运行时初始化等真实部署开销与进程外结果属于不同测量口径。更完整的工具链总览包括多框架运行、自定义 CoreRun、NativeAOT、Wasm 等可参考 docs/articles/configs/toolchains.md其中也内嵌了本示例与IntroInProcessWrongEnv示例的文档页。总结InProcess 工具链是 BenchmarkDotNet 在进程级隔离与极速/特殊运行时支持之间提供的另一条路径InProcessEmitToolchain通过动态发射 IL 在宿主进程内直接运行基准InProcessNoEmitToolchain则基于反射服务于 AOT 等无法发射 IL 的环境。通过[InProcess]特性或Job.WithToolchain(InProcessEmitToolchain.Default)可以一键接入配合InProcessValidator校验环境一致性、MemoryDiagnoser观察分配差异你可以在不牺牲 BenchmarkDotNet 测量框架的前提下获得接近零启动成本的基准运行体验——尤其适合作为 CI 中的快速冒烟基准或在自研运行时上完成基准验证。赞分享性能测试开发工具【免费下载链接】BenchmarkDotNetPowerful .NET library for benchmarking项目地址https://gitcode.com/gh_mirrors/be/BenchmarkDotNet点击查看免费下载相关推荐XMRig 内嵌基准测试与压力测试完全指南--bench / --stress 实战与原理剖析XMRig 内嵌基准测试与压力测试完全指南 bench / stress 实战与原理剖析 本篇技术指南围绕 XMRig 内置的嵌入式基准测试Embedded区块链高性能计算Terminal.Gui 性能基准测试完全指南内存 Profiler、BenchmarkDotNet 与 CI 性能门禁Terminal.Gui 性能基准测试完全指南内存 Profiler、BenchmarkDotNet 与 CI 性能门禁 Terminal.Gui 是面向 .UI组件跨平台桌面应用Garnet 基准测试指南从 RESP 端到端压测到 BenchmarkDotNet 微基准与 Tsavorite 设备层Garnet 基准测试指南从 RESP 端到端压测到 BenchmarkDotNet 微基准与 Tsavorite 设备层 Garnet 仓库的 benchm缓存KV存储后端上一篇从Benthos到Redpanda Connect流处理架构的十年演进之路下一篇用 generator-react-native-vector-icons 从上游字体一键生成独立图标包创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考