From f650bc57768f65fa1320453e675a50e8c174c0d2 Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Mon, 11 May 2026 12:49:28 +0800
Subject: [PATCH] =?UTF-8?q?test(cqrs-benchmarks):=20=E6=96=B0=E5=A2=9E=20n?=
=?UTF-8?q?otification=20startup=20=E5=9F=BA=E5=87=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 NotificationStartupBenchmarks,补齐 notification 的 Initialization 与 ColdStart 对称矩阵
- 复用最小宿主搭建路径,对齐 GFramework.CQRS、Mediator 与 MediatR 的单处理器首击发布对照
- 修复 cold-start 方法的资源释放时序,确保 benchmark 构建零警告通过
---
.../NotificationStartupBenchmarks.cs | 289 ++++++++++++++++++
1 file changed, 289 insertions(+)
create mode 100644 GFramework.Cqrs.Benchmarks/Messaging/NotificationStartupBenchmarks.cs
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/NotificationStartupBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/NotificationStartupBenchmarks.cs
new file mode 100644
index 00000000..5ede14f1
--- /dev/null
+++ b/GFramework.Cqrs.Benchmarks/Messaging/NotificationStartupBenchmarks.cs
@@ -0,0 +1,289 @@
+// Copyright (c) 2025-2026 GeWuYou
+// SPDX-License-Identifier: Apache-2.0
+
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Columns;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Order;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Core.Ioc;
+using GFramework.Core.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using MediatR;
+using Microsoft.Extensions.DependencyInjection;
+using ILogger = GFramework.Core.Abstractions.Logging.ILogger;
+using GeneratedMediator = Mediator.Mediator;
+
+namespace GFramework.Cqrs.Benchmarks.Messaging;
+
+///
+/// 对比 notification 宿主在 GFramework.CQRS、NuGet `Mediator` 与 MediatR 之间的初始化与首次发布成本。
+///
+///
+/// 该矩阵刻意保持“单 notification + 单 handler + 最小宿主”的对称形状,
+/// 只观察宿主构建与首个 publish 命中的额外开销,不把 fan-out 或自定义发布策略混入 startup 结论。
+///
+[Config(typeof(Config))]
+public class NotificationStartupBenchmarks
+{
+ private static readonly ILogger RuntimeLogger = CreateLogger(nameof(NotificationStartupBenchmarks));
+ private static readonly BenchmarkNotification Notification = new(Guid.NewGuid());
+
+ private MicrosoftDiContainer _container = null!;
+ private ICqrsRuntime _runtime = null!;
+ private ServiceProvider _serviceProvider = null!;
+ private IPublisher _publisher = null!;
+ private ServiceProvider _mediatorServiceProvider = null!;
+ private GeneratedMediator _mediator = null!;
+
+ ///
+ /// 配置 notification startup benchmark 的公共输出格式。
+ ///
+ private sealed class Config : ManualConfig
+ {
+ public Config()
+ {
+ AddJob(Job.Default
+ .WithId("ColdStart")
+ .WithInvocationCount(1)
+ .WithUnrollFactor(1));
+ AddColumnProvider(DefaultColumnProviders.Instance);
+ AddColumn(new CustomColumn("Scenario", static (_, _) => "NotificationStartup"), TargetMethodColumn.Method, CategoriesColumn.Default);
+ AddDiagnoser(MemoryDiagnoser.Default);
+ AddLogicalGroupRules(BenchmarkLogicalGroupRule.ByCategory);
+ WithOrderer(new DefaultOrderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared));
+ }
+ }
+
+ ///
+ /// 构建 startup benchmark 复用的最小 notification 宿主对象。
+ ///
+ [GlobalSetup]
+ public void Setup()
+ {
+ Fixture.Setup("NotificationStartup", handlerCount: 1, pipelineCount: 0);
+
+ _serviceProvider = CreateMediatRServiceProvider();
+ _publisher = _serviceProvider.GetRequiredService();
+
+ _mediatorServiceProvider = CreateMediatorServiceProvider();
+ _mediator = _mediatorServiceProvider.GetRequiredService();
+
+ _container = CreateGFrameworkContainer();
+ _runtime = CreateGFrameworkRuntime(_container);
+ }
+
+ ///
+ /// 在每次 cold-start 迭代前清空 dispatcher 静态缓存,确保每组 benchmark 都重新命中首次绑定路径。
+ ///
+ [IterationSetup]
+ public void ResetColdStartCaches()
+ {
+ BenchmarkDispatcherCacheHelper.ClearDispatcherCaches();
+ }
+
+ ///
+ /// 释放 startup benchmark 复用的宿主对象。
+ ///
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ BenchmarkCleanupHelper.DisposeAll(_container, _serviceProvider, _mediatorServiceProvider);
+ }
+
+ ///
+ /// 返回已构建宿主中的 MediatR publisher,作为 initialization 组的句柄解析 baseline。
+ ///
+ /// 当前 benchmark 复用的 MediatR publisher。
+ [Benchmark(Baseline = true)]
+ [BenchmarkCategory("Initialization")]
+ public IPublisher Initialization_MediatR()
+ {
+ return _publisher;
+ }
+
+ ///
+ /// 返回已构建宿主中的 GFramework.CQRS runtime,确保与 MediatR baseline 处于相同初始化阶段。
+ ///
+ /// 当前 benchmark 复用的 GFramework.CQRS runtime。
+ [Benchmark]
+ [BenchmarkCategory("Initialization")]
+ public ICqrsRuntime Initialization_GFrameworkCqrs()
+ {
+ return _runtime;
+ }
+
+ ///
+ /// 返回已构建宿主中的 `Mediator` concrete mediator,作为 source-generated 对照组的初始化句柄。
+ ///
+ /// 当前 benchmark 复用的 `Mediator` concrete mediator。
+ [Benchmark]
+ [BenchmarkCategory("Initialization")]
+ public GeneratedMediator Initialization_Mediator()
+ {
+ return _mediator;
+ }
+
+ ///
+ /// 在新宿主上首次发布 notification,作为 MediatR 的 cold-start baseline。
+ ///
+ /// 代表首次 publish 完成的任务。
+ [Benchmark(Baseline = true)]
+ [BenchmarkCategory("ColdStart")]
+ public async Task ColdStart_MediatR()
+ {
+ using var serviceProvider = CreateMediatRServiceProvider();
+ var publisher = serviceProvider.GetRequiredService();
+ await publisher.Publish(Notification, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// 在新 runtime 上首次发布 notification,量化 GFramework.CQRS 的 first-hit 成本。
+ ///
+ /// 代表首次 publish 完成的值任务。
+ [Benchmark]
+ [BenchmarkCategory("ColdStart")]
+ public async ValueTask ColdStart_GFrameworkCqrs()
+ {
+ using var container = CreateGFrameworkContainer();
+ var runtime = CreateGFrameworkRuntime(container);
+ await runtime.PublishAsync(BenchmarkContext.Instance, Notification, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// 在新的 `Mediator` 宿主上首次发布 notification,量化 source-generated concrete path 的 cold-start 成本。
+ ///
+ /// 代表首次 publish 完成的值任务。
+ [Benchmark]
+ [BenchmarkCategory("ColdStart")]
+ public async ValueTask ColdStart_Mediator()
+ {
+ using var serviceProvider = CreateMediatorServiceProvider();
+ var mediator = serviceProvider.GetRequiredService();
+ await mediator.Publish(Notification, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// 构建只承载当前 benchmark notification 的最小 GFramework.CQRS runtime。
+ ///
+ ///
+ /// startup benchmark 只需要验证单 handler publish 的首击路径,
+ /// 因此这里继续使用单点手工注册,避免把更广泛的注册协调逻辑混入结果。
+ ///
+ private static MicrosoftDiContainer CreateGFrameworkContainer()
+ {
+ return BenchmarkHostFactory.CreateFrozenGFrameworkContainer(static container =>
+ {
+ container.RegisterTransient, BenchmarkNotificationHandler>();
+ });
+ }
+
+ ///
+ /// 基于已冻结的 benchmark 容器构建最小 GFramework.CQRS runtime。
+ ///
+ /// 当前 benchmark 拥有并负责释放的容器。
+ /// 可直接发布 notification 的 runtime。
+ private static ICqrsRuntime CreateGFrameworkRuntime(MicrosoftDiContainer container)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+ return GFramework.Cqrs.CqrsRuntimeFactory.CreateRuntime(container, RuntimeLogger);
+ }
+
+ ///
+ /// 构建只承载当前 benchmark notification handler 的最小 MediatR 对照宿主。
+ ///
+ /// 可直接解析 的 DI 宿主。
+ private static ServiceProvider CreateMediatRServiceProvider()
+ {
+ return BenchmarkHostFactory.CreateMediatRServiceProvider(
+ configure: null,
+ typeof(NotificationStartupBenchmarks),
+ static candidateType => candidateType == typeof(BenchmarkNotificationHandler),
+ ServiceLifetime.Transient);
+ }
+
+ ///
+ /// 构建只承载当前 benchmark notification handler 的最小 `Mediator` 对照宿主。
+ ///
+ /// 可直接解析 generated `Mediator.Mediator` 的 DI 宿主。
+ private static ServiceProvider CreateMediatorServiceProvider()
+ {
+ return BenchmarkHostFactory.CreateMediatorServiceProvider(configure: null);
+ }
+
+ ///
+ /// 为 benchmark 创建稳定的 fatal 级 logger,避免把日志成本混入 startup 测量。
+ ///
+ /// logger 分类名。
+ /// 当前 benchmark 使用的稳定 logger。
+ private static ILogger CreateLogger(string categoryName)
+ {
+ LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider
+ {
+ MinLevel = LogLevel.Fatal
+ };
+ return LoggerFactoryResolver.Provider.CreateLogger(categoryName);
+ }
+
+ ///
+ /// Benchmark notification。
+ ///
+ /// 通知标识。
+ public sealed record BenchmarkNotification(Guid Id) :
+ GFramework.Cqrs.Abstractions.Cqrs.INotification,
+ Mediator.INotification,
+ MediatR.INotification;
+
+ ///
+ /// 同时实现 GFramework.CQRS、NuGet `Mediator` 与 MediatR 契约的最小 notification handler。
+ ///
+ public sealed class BenchmarkNotificationHandler :
+ GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler,
+ Mediator.INotificationHandler,
+ MediatR.INotificationHandler
+ {
+ ///
+ /// 处理 GFramework.CQRS notification。
+ ///
+ /// 当前 notification。
+ /// 取消令牌。
+ /// 表示处理完成的值任务。
+ public ValueTask Handle(BenchmarkNotification notification, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(notification);
+ cancellationToken.ThrowIfCancellationRequested();
+ return ValueTask.CompletedTask;
+ }
+
+ ///
+ /// 处理 NuGet `Mediator` notification。
+ ///
+ /// 当前 notification。
+ /// 取消令牌。
+ /// 表示处理完成的值任务。
+ ValueTask Mediator.INotificationHandler.Handle(
+ BenchmarkNotification notification,
+ CancellationToken cancellationToken)
+ {
+ return Handle(notification, cancellationToken);
+ }
+
+ ///
+ /// 处理 MediatR notification。
+ ///
+ /// 当前 notification。
+ /// 取消令牌。
+ /// 表示处理完成的任务。
+ Task MediatR.INotificationHandler.Handle(
+ BenchmarkNotification notification,
+ CancellationToken cancellationToken)
+ {
+ return Handle(notification, cancellationToken).AsTask();
+ }
+ }
+}