mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-09 10:19:00 +08:00
test(cqrs): 补充 notification publisher fan-out 基准对照
- 新增默认顺序发布器与 TaskWhenAllNotificationPublisher 的 fixed 4 handler fan-out benchmark 对照 - 更新 benchmark README 与 cqrs-rewrite 恢复文档,记录 RP-114 的性能结论与下一步
This commit is contained in:
parent
7ff4b628a1
commit
b0102b5206
@ -14,6 +14,7 @@ using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Ioc;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
using GFramework.Cqrs.Notification;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using GeneratedMediator = Mediator.Mediator;
|
||||
@ -28,7 +29,8 @@ namespace GFramework.Cqrs.Benchmarks.Messaging;
|
||||
public class NotificationFanOutBenchmarks
|
||||
{
|
||||
private MicrosoftDiContainer _container = null!;
|
||||
private ICqrsRuntime _runtime = null!;
|
||||
private ICqrsRuntime _sequentialRuntime = null!;
|
||||
private ICqrsRuntime _taskWhenAllRuntime = null!;
|
||||
private ServiceProvider _mediatrServiceProvider = null!;
|
||||
private ServiceProvider _mediatorServiceProvider = null!;
|
||||
private IPublisher _mediatrPublisher = null!;
|
||||
@ -78,9 +80,13 @@ public class NotificationFanOutBenchmarks
|
||||
container.RegisterSingleton<GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler<BenchmarkNotification>, BenchmarkNotificationHandler3>();
|
||||
container.RegisterSingleton<GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler<BenchmarkNotification>, BenchmarkNotificationHandler4>();
|
||||
});
|
||||
_runtime = GFramework.Cqrs.CqrsRuntimeFactory.CreateRuntime(
|
||||
_sequentialRuntime = GFramework.Cqrs.CqrsRuntimeFactory.CreateRuntime(
|
||||
_container,
|
||||
LoggerFactoryResolver.Provider.CreateLogger(nameof(NotificationFanOutBenchmarks)));
|
||||
_taskWhenAllRuntime = GFramework.Cqrs.CqrsRuntimeFactory.CreateRuntime(
|
||||
_container,
|
||||
LoggerFactoryResolver.Provider.CreateLogger($"{nameof(NotificationFanOutBenchmarks)}.{nameof(TaskWhenAllNotificationPublisher)}"),
|
||||
new TaskWhenAllNotificationPublisher());
|
||||
|
||||
_mediatrServiceProvider = BenchmarkHostFactory.CreateMediatRServiceProvider(
|
||||
services =>
|
||||
@ -127,12 +133,21 @@ public class NotificationFanOutBenchmarks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过 GFramework.CQRS runtime 发布固定 4 处理器的 notification。
|
||||
/// 通过默认顺序发布器的 GFramework.CQRS runtime 发布固定 4 处理器的 notification。
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ValueTask PublishNotification_GFrameworkCqrs()
|
||||
public ValueTask PublishNotification_GFrameworkCqrsSequential()
|
||||
{
|
||||
return _runtime.PublishAsync(BenchmarkContext.Instance, _notification, CancellationToken.None);
|
||||
return _sequentialRuntime.PublishAsync(BenchmarkContext.Instance, _notification, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过内置 <c>Task.WhenAll(...)</c> 发布器的 GFramework.CQRS runtime 发布固定 4 处理器的 notification。
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ValueTask PublishNotification_GFrameworkCqrsTaskWhenAll()
|
||||
{
|
||||
return _taskWhenAllRuntime.PublishAsync(BenchmarkContext.Instance, _notification, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
- `Messaging/NotificationBenchmarks.cs`
|
||||
- `GFramework.Cqrs` runtime、NuGet `Mediator` source-generated concrete path 与 `MediatR` 的单处理器 notification publish 对比
|
||||
- `Messaging/NotificationFanOutBenchmarks.cs`
|
||||
- fixed `4 handler` notification fan-out 的 baseline、`GFramework.Cqrs` runtime、NuGet `Mediator` source-generated concrete path 与 `MediatR` publish 对比
|
||||
- fixed `4 handler` notification fan-out 的 baseline、`GFramework.Cqrs` 默认顺序发布器、内置 `TaskWhenAllNotificationPublisher`、NuGet `Mediator` source-generated concrete path 与 `MediatR` publish 对比
|
||||
- `Messaging/StreamingBenchmarks.cs`
|
||||
- direct handler、已接上 handwritten generated stream invoker provider 的 `GFramework.Cqrs` runtime 与 `MediatR` 的 stream request 完整枚举对比
|
||||
|
||||
|
||||
@ -7,10 +7,14 @@ CQRS 迁移与收敛。
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`CQRS-REWRITE-RP-113`
|
||||
- 恢复点编号:`CQRS-REWRITE-RP-114`
|
||||
- 当前阶段:`Phase 8`
|
||||
- 当前 PR 锚点:`PR #341`
|
||||
- 当前结论:
|
||||
- 当前 `RP-114` 已继续沿用 `$gframework-batch-boot 50`,并沿着 `RP-113` 刚落地的 notification publisher 能力切片继续补 benchmark:`NotificationFanOutBenchmarks` 现同时纳入 `GFramework.Cqrs` 默认顺序发布器与新内置 `TaskWhenAllNotificationPublisher`,用于量化“能力差距收口后,固定 4 handler fan-out 的成本变化”
|
||||
- `RP-114` 的 short-job 结果显示:fixed `4 handler` fan-out 下,默认顺序发布器约 `427.453 ns / 408 B`,内置 `TaskWhenAllNotificationPublisher` 约 `472.574 ns / 496 B`,`MediatR` 约 `225.940 ns / 1256 B`,NuGet `Mediator` concrete runtime 约 `3.854 ns / 0 B`;这说明当前内置并行 publisher 的主要价值是语义补齐,而不是 steady-state fan-out 性能收益
|
||||
- 这一批保持 runtime 公开 API 与 notification 语义不变,只扩 benchmark 对照口径与恢复文档;原因是 `RP-113` 已经把并行 publisher 能力落到 production path,当前更高价值的是先证明它相对默认顺序发布器、`Mediator` 与 `MediatR` 的成本位置,而不是立即继续扩第二个 publisher strategy
|
||||
- 当前分支相对 `origin/main` 的累计 branch diff 启动时为 `9 files`,仍明显低于 `$gframework-batch-boot 50` 的停止阈值;这一批继续保持单模块、低风险、可直接评审的 benchmark 边界
|
||||
- 当前 `RP-113` 已继续沿用 `$gframework-batch-boot 50`,并把 notification 线从 benchmark 对照推进到实际 runtime 能力:新增公开内置 `TaskWhenAllNotificationPublisher`,让 `GFramework.Cqrs` 在保留默认顺序发布器的同时,提供与 `Mediator` `TaskWhenAllPublisher` 对齐的并行 notification publish 策略
|
||||
- `TaskWhenAllNotificationPublisher` 当前语义明确为:零处理器静默完成,单处理器直接透传,多处理器并行启动并等待全部结束;它不保留默认顺序发布器的“首个异常立即停止”语义,而是把全部处理器的失败/取消结果收敛到同一个返回任务
|
||||
- 本轮同时补齐 `CqrsNotificationPublisherTests` 对新内置策略的回归,并更新 `GFramework.Cqrs/README.md` 与 `docs/zh-CN/core/cqrs.md`,把切换方式和语义边界写回用户可见文档;当前已提交 branch diff 仍明显低于 `$gframework-batch-boot 50` 的停止阈值
|
||||
@ -365,8 +369,8 @@ CQRS 迁移与收敛。
|
||||
|
||||
## 下一推荐步骤
|
||||
|
||||
1. 当前 turn 仍可继续自动推进;若下一批继续沿用 `$gframework-batch-boot 50` 且仍留在 notification 线,优先补 `TaskWhenAllNotificationPublisher` 的 benchmark / 异常聚合文档细化,或继续评估是否需要第二个内置 publisher strategy,而不是回头再加同层级 fan-out 对照
|
||||
2. 若下一轮要切回更高价值热点,优先重新审视 request dispatch 常量开销或 notification publisher 策略配置面,而不是新增 generated notification invoker/provider 这一类 steady-state 收益信号偏弱的 runtime seam
|
||||
1. 若 `RP-114` 的 fan-out benchmark 证实内置 `TaskWhenAllNotificationPublisher` 在固定多处理器场景下明显优于默认顺序发布器,可继续评估是否需要补单处理器 / 异常路径的对照或把这组结果吸收到用户文档
|
||||
2. 当前 benchmark 已证明 `TaskWhenAllNotificationPublisher` 的价值主要在并行完成与异常聚合语义,而不是吞吐收益;下一轮优先评估 notification publisher 配置面 / 文档边界,或回到 request dispatch 常量开销,而不是新增 generated notification invoker/provider 这类 steady-state 收益信号偏弱的 runtime seam
|
||||
3. 若 benchmark 对照需要继续贴近 `Mediator` 官方设计,再评估 `Mediator` 的 compile-time lifetime / stream 对照矩阵,或给 stream 引入 scoped host 基线,而不是回头重试已被 benchmark 否决的 `GetAll(Type)` 零行为探测方案
|
||||
|
||||
## 活跃文档
|
||||
|
||||
@ -2,6 +2,30 @@
|
||||
|
||||
## 2026-05-08
|
||||
|
||||
### 阶段:`TaskWhenAll` notification publisher fan-out benchmark(CQRS-REWRITE-RP-114)
|
||||
|
||||
- 延续 `$gframework-batch-boot 50`,本轮不再扩新的 notification runtime 能力,而是沿着 `RP-113` 刚落地的内置并行 publisher 继续补验证口径:
|
||||
- 当前分支相对 `origin/main`(`7ca21af9`, `2026-05-08 16:12:20 +0800`)的累计 branch diff 启动时为 `9 files`,明显低于 `50` 文件阈值
|
||||
- `RP-112` 只量化了默认顺序发布器的 fixed `4 handler` fan-out 成本;`RP-113` 已把 `TaskWhenAllNotificationPublisher` 引入 production runtime,但还没有 benchmark 说明“能力差距收口后,代价是多少”
|
||||
- 本轮主线程决策:
|
||||
- 在 `GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs` 同时保留 `baseline`、默认顺序 `GFramework.Cqrs`、内置 `TaskWhenAllNotificationPublisher`、NuGet `Mediator` concrete runtime 与 `MediatR` 五组对照
|
||||
- 复用同一个冻结 `MicrosoftDiContainer` 创建两个 `ICqrsRuntime`,确保变量集中在 notification publisher 策略,而不是 handler 注册或容器形状差异
|
||||
- 更新 `GFramework.Cqrs.Benchmarks/README.md` 与 active tracking,使默认恢复入口直接记录新的 benchmark 口径
|
||||
- 本轮权威验证:
|
||||
- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
|
||||
- 结果:通过,`0 warning / 0 error`
|
||||
- `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --filter "*NotificationFanOutBenchmarks*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
|
||||
- 结果:通过
|
||||
- 备注:fixed `4 handler` fan-out 对照当前约为 baseline `7.424 ns / 0 B`、`Mediator` `3.854 ns / 0 B`、`MediatR` `225.940 ns / 1256 B`、`GFramework.Cqrs` 默认顺序发布器 `427.453 ns / 408 B`、内置 `TaskWhenAllNotificationPublisher` `472.574 ns / 496 B`
|
||||
- `python3 scripts/license-header.py --check --paths GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs GFramework.Cqrs.Benchmarks/README.md ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
|
||||
- 结果:通过
|
||||
- `git diff --check`
|
||||
- 结果:通过
|
||||
- 本轮结论:
|
||||
- 当前 benchmark 说明 `TaskWhenAllNotificationPublisher` 的主要价值是补齐“等待全部处理器并聚合异常”的 notification 语义,而不是在 fixed `4 handler` fan-out steady-state 下带来吞吐收益;它比默认顺序发布器额外增加了约 `45 ns` 与 `88 B`
|
||||
- 这组结果足以支持后续把 notification 线的重心转回 API 配置面、使用边界与文档语义,而不是继续机械堆新的 runtime seam 或期待 `TaskWhenAll` 自带性能红利
|
||||
- 当前 turn 仍可继续自动推进,但默认停止规则仍以“上下文预算优先、单批可评审边界次之”为准
|
||||
|
||||
### 阶段:内置 `TaskWhenAll` notification publisher(CQRS-REWRITE-RP-113)
|
||||
|
||||
- 延续 `$gframework-batch-boot 50`,本轮不再继续堆 notification benchmark 维度,而是直接把上一批已经量化清楚的 capability gap 收口到 runtime:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user