// 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.Reflection; 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; namespace GFramework.Cqrs.Benchmarks.Messaging; /// /// 对比 request 宿主的初始化与首次分发成本,作为后续吸收 `Mediator` comparison benchmark 设计的 startup 基线。 /// [Config(typeof(Config))] public class RequestStartupBenchmarks { private static readonly ILogger RuntimeLogger = CreateLogger(nameof(RequestStartupBenchmarks)); private static readonly BenchmarkRequest Request = new(Guid.NewGuid()); private ServiceProvider _serviceProvider = null!; private IMediator _mediatr = null!; /// /// 配置 request startup benchmark 的公共输出格式。 /// private sealed class Config : ManualConfig { public Config() { AddJob(Job.Default); AddColumnProvider(DefaultColumnProviders.Instance); AddColumn(new CustomColumn("Scenario", static (_, _) => "RequestStartup")); AddDiagnoser(MemoryDiagnoser.Default); WithOrderer(new DefaultOrderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared)); } } /// /// 构建 steady-state 初始化 benchmark 复用的宿主对象。 /// [GlobalSetup] public void Setup() { Fixture.Setup("RequestStartup", handlerCount: 1, pipelineCount: 0); _serviceProvider = CreateMediatRServiceProvider(); _mediatr = _serviceProvider.GetRequiredService(); } /// /// 释放 MediatR 对照组使用的 DI 宿主。 /// [GlobalCleanup] public void Cleanup() { _serviceProvider.Dispose(); } /// /// 解析 MediatR mediator,作为 startup 句柄解析成本的 baseline。 /// [Benchmark(Baseline = true)] [BenchmarkCategory("Initialization")] public IMediator Initialization_MediatR() { return _serviceProvider.GetRequiredService(); } /// /// 创建 GFramework.CQRS runtime,作为同层级 startup 句柄创建成本的对照。 /// [Benchmark] [BenchmarkCategory("Initialization")] public ICqrsRuntime Initialization_GFrameworkCqrs() { return CreateGFrameworkRuntime(); } /// /// 在新宿主上首次发送 request,作为 MediatR 的 cold-start baseline。 /// [Benchmark(Baseline = true)] [BenchmarkCategory("ColdStart")] public async Task ColdStart_MediatR() { using var serviceProvider = CreateMediatRServiceProvider(); var mediator = serviceProvider.GetRequiredService(); return await mediator.Send(Request, CancellationToken.None).ConfigureAwait(false); } /// /// 在清空 dispatcher 静态缓存后,于新宿主上首次发送 request,量化 GFramework.CQRS 的 first-hit 成本。 /// [Benchmark] [BenchmarkCategory("ColdStart")] public ValueTask ColdStart_GFrameworkCqrs() { ClearDispatcherCaches(); var runtime = CreateGFrameworkRuntime(); return runtime.SendAsync(BenchmarkContext.Instance, Request, CancellationToken.None); } /// /// 构建只承载当前 benchmark request 的最小 GFramework.CQRS runtime。 /// private static ICqrsRuntime CreateGFrameworkRuntime() { var container = new MicrosoftDiContainer(); container.RegisterTransient, BenchmarkRequestHandler>(); return GFramework.Cqrs.CqrsRuntimeFactory.CreateRuntime(container, RuntimeLogger); } /// /// 构建只承载当前 benchmark request 的最小 MediatR 对照宿主。 /// private static ServiceProvider CreateMediatRServiceProvider() { var services = new ServiceCollection(); services.AddSingleton, BenchmarkRequestHandler>(); services.AddMediatR(static options => options.RegisterServicesFromAssembly(typeof(RequestStartupBenchmarks).Assembly)); return services.BuildServiceProvider(); } /// /// 为 benchmark 创建稳定的 fatal 级 logger,避免把日志成本混入 startup 测量。 /// private static ILogger CreateLogger(string categoryName) { LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Fatal }; return LoggerFactoryResolver.Provider.CreateLogger(categoryName); } /// /// 清空 dispatcher 静态缓存,避免同一进程中的前一轮分发污染 cold-start 结果。 /// private static void ClearDispatcherCaches() { ClearDispatcherCache("NotificationDispatchBindings"); ClearDispatcherCache("RequestDispatchBindings"); ClearDispatcherCache("StreamDispatchBindings"); ClearDispatcherCache("GeneratedRequestInvokers"); ClearDispatcherCache("GeneratedStreamInvokers"); } /// /// 通过反射定位并清空 dispatcher 的指定缓存字段。 /// /// 要清理的静态缓存字段名。 private static void ClearDispatcherCache(string fieldName) { var field = typeof(GFramework.Cqrs.CqrsRuntimeFactory).Assembly .GetType("GFramework.Cqrs.Internal.CqrsDispatcher", throwOnError: true)! .GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static) ?? throw new InvalidOperationException($"Missing dispatcher cache field {fieldName}."); var cache = field.GetValue(null) ?? throw new InvalidOperationException($"Dispatcher cache field {fieldName} returned null."); var clearMethod = cache.GetType().GetMethod("Clear", BindingFlags.Public | BindingFlags.Instance) ?? throw new InvalidOperationException( $"Dispatcher cache field {fieldName} does not expose a Clear method."); _ = clearMethod.Invoke(cache, null); } /// /// Benchmark request。 /// /// 请求标识。 public sealed record BenchmarkRequest(Guid Id) : GFramework.Cqrs.Abstractions.Cqrs.IRequest, MediatR.IRequest; /// /// Benchmark response。 /// /// 响应标识。 public sealed record BenchmarkResponse(Guid Id); /// /// 同时实现 GFramework.CQRS 与 MediatR 契约的最小 request handler。 /// public sealed class BenchmarkRequestHandler : GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler, MediatR.IRequestHandler { /// /// 处理 GFramework.CQRS request。 /// public ValueTask Handle(BenchmarkRequest request, CancellationToken cancellationToken) { return ValueTask.FromResult(new BenchmarkResponse(request.Id)); } /// /// 处理 MediatR request。 /// Task MediatR.IRequestHandler.Handle( BenchmarkRequest request, CancellationToken cancellationToken) { return Task.FromResult(new BenchmarkResponse(request.Id)); } } }