mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-11 20:38:58 +08:00
- 新增 NotificationStartupBenchmarks,补齐 notification 的 Initialization 与 ColdStart 对称矩阵 - 复用最小宿主搭建路径,对齐 GFramework.CQRS、Mediator 与 MediatR 的单处理器首击发布对照 - 修复 cold-start 方法的资源释放时序,确保 benchmark 构建零警告通过
GFramework.Cqrs.Benchmarks
该模块承载 GFramework.Cqrs 的独立性能基准工程,用于在当前 HEAD 上复核 request、stream、notification 的 steady-state 与 startup 成本边界。
目的
- 为
GFramework.Cqrs提供独立于测试工程的 BenchmarkDotNet 复核入口 - 让 request、stream、notification 的热路径与 cold-start 变化有可重复的对照矩阵
- 在不引入“未来已存在”假设的前提下,明确当前 benchmark 已覆盖什么、还没有覆盖什么
当前 coverage
当前工程已经覆盖以下矩阵:
- request steady-state
Messaging/RequestBenchmarks.cs- direct handler、默认
GFramework.Cqrsruntime、NuGetMediatorsource-generated concrete path、MediatR
- direct handler、默认
Messaging/RequestLifetimeBenchmarks.csSingleton / Scoped / Transient三类 handler 生命周期下,baseline、默认 generated-provider 宿主接线的GFramework.Cqrsruntime 与MediatR
Messaging/RequestPipelineBenchmarks.cs0 / 1 / 4个 pipeline 行为下,baseline、默认 generated-provider 宿主接线的GFramework.Cqrsruntime 与MediatR
Messaging/RequestInvokerBenchmarks.cs- baseline、
GFramework.Cqrsreflection request binding、GFramework.Cqrsgenerated request invoker、MediatR
- baseline、
- request startup
Messaging/RequestStartupBenchmarks.csInitialization与ColdStart两组下,GFramework.Cqrs、NuGetMediator、MediatR
- stream steady-state
Messaging/StreamingBenchmarks.cs- baseline、默认 generated-provider 宿主接线的
GFramework.Cqrsruntime 与MediatR - 同时提供
FirstItem与DrainAll两种观测口径
- baseline、默认 generated-provider 宿主接线的
Messaging/StreamLifetimeBenchmarks.csSingleton / Scoped / Transient三类 handler 生命周期下,baseline、GFramework.Cqrsreflection stream binding、GFramework.Cqrsgenerated stream registry、MediatR- 同时提供
FirstItem与DrainAll两种观测口径
Messaging/StreamInvokerBenchmarks.cs- baseline、
GFramework.Cqrsreflection stream binding、GFramework.Cqrsgenerated stream invoker、MediatR - 同时提供
FirstItem与DrainAll两种观测口径
- baseline、
- stream startup
Messaging/StreamStartupBenchmarks.csInitialization与ColdStart两组下,GFramework.Cqrsreflection、GFramework.Cqrsgenerated、MediatR- 其中
ColdStart的边界是“新宿主 + 首个元素命中”,不是完整枚举整个 stream
- notification
Messaging/NotificationBenchmarks.cs- 单处理器 publish 下,
GFramework.Cqrsruntime、NuGetMediatorsource-generated concrete path、MediatR
- 单处理器 publish 下,
Messaging/NotificationLifetimeBenchmarks.cs- 单处理器 publish 在
Singleton / Scoped / Transient三类 handler 生命周期下的 baseline、GFramework.Cqrs与MediatR对照
- 单处理器 publish 在
Messaging/NotificationFanOutBenchmarks.cs- 固定
4 handlerfan-out 下的 baseline、GFramework.Cqrs默认顺序发布器、内置TaskWhenAllNotificationPublisher、NuGetMediator、MediatR
- 固定
最小使用方式
dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release
上面的命令只验证 benchmark 工程当前可以正常编译。
如需实际运行 benchmark,再执行:
dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release
如需只复核某一类场景,可把 BenchmarkDotNet 参数放在 -- 之后,例如:
dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --filter "*RequestLifetimeBenchmarks.SendRequest_*"
dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --filter "*StreamLifetimeBenchmarks.Stream_*"
并发运行约束
当两个 benchmark 进程需要并发运行时,必须为每个进程追加不同的 --artifacts-suffix <suffix>。当前入口会把这个 suffix 解析成独立的 BenchmarkDotNet.Artifacts/<suffix>/ 目录,并在该目录下复制隔离的 benchmark host,避免多个进程写入同一份 auto-generated build 与 artifacts 输出。
例如:
dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix req-lifetime-a --filter "*RequestLifetimeBenchmarks.SendRequest_*"
dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix stream-lifetime-b --filter "*StreamLifetimeBenchmarks.Stream_*"
如果不并发运行,就不需要额外传入 --artifacts-suffix。BenchmarkDotNet.Artifacts/ 仍然是本地生成输出,默认不作为常规提交内容。
结果解读边界
RequestLifetimeBenchmarks的Scoped场景会在每次 request 分发时显式创建并释放真实 DI 作用域;它观察的是 scoped handler 的解析与 dispatch 成本,不把 runtime 构造常量成本混入生命周期对照NotificationLifetimeBenchmarks的Scoped场景也采用真实 DI 作用域;它比较的是 publish 路径上的生命周期额外开销,不是根容器解析退化后的近似值StreamingBenchmarks、StreamLifetimeBenchmarks、StreamInvokerBenchmarks同时暴露FirstItem与DrainAllFirstItem适合观察“建流到首个元素”的固定成本DrainAll适合观察完整枚举整个 stream 的总成本
StreamStartupBenchmarks的ColdStart只推进到首个元素,因此它回答的是“新宿主下首次建流命中”的边界,不回答完整枚举总成本- 当前 HEAD 没有单独固化的 short-job benchmark 类或 checked-in short-job 结果;如果手动使用 short job / short run 只做 smoke 复核,应把它理解为“确认矩阵与路径能跑通”
- 特别是
StreamInvokerBenchmarks的DrainAll在 short-job smoke 下不应直接写成 reflection、generated 或MediatR之间的稳定排序结论;若要比较名次或小幅差值,应复跑默认作业或更完整的批次
当前缺口
- 当前没有 stream 版的 NuGet
Mediatorsource-generated concrete path 对照;stream steady-state、lifetime、startup 现在都只覆盖GFramework.Cqrs与MediatR - 当前没有 request 生命周期下的 NuGet
Mediatorcompile-time lifetime 矩阵;RequestLifetimeBenchmarks只覆盖GFramework.Cqrs与MediatR - 当前没有 notification startup / cold-start benchmark;notification 只覆盖 steady-state 单处理器、生命周期、固定
4 handlerfan-out - 当前没有 notification fan-out 的生命周期矩阵;
NotificationFanOutBenchmarks只覆盖固定4 handler的已装配宿主 - 当前没有 stream pipeline benchmark;现有 pipeline coverage 仅限 request