From d9547dae4b4877eb96ba9df038a2053bdb4687e2 Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Fri, 8 May 2026 12:23:05 +0800
Subject: [PATCH] =?UTF-8?q?perf(cqrs):=20=E6=94=B6=E5=8F=A3=E9=BB=98?=
=?UTF-8?q?=E8=AE=A4=E8=AF=B7=E6=B1=82=E5=9F=BA=E5=87=86=E5=AE=BF=E4=B8=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 handwritten generated request registry,并让默认 RequestBenchmarks 通过真实程序集注册路径接上 generated invoker provider
- 补齐 benchmark 最小宿主所需的 CQRS runtime、registrar 与 registration service 基础设施接线
- 更新 CQRS 迁移 tracking 与 trace,记录 RP-105 的 benchmark 结论和当前恢复点
---
.../Messaging/BenchmarkHostFactory.cs | 45 ++++++++
...eneratedDefaultRequestBenchmarkRegistry.cs | 100 ++++++++++++++++++
.../Messaging/RequestBenchmarks.cs | 13 ++-
GFramework.Cqrs.Benchmarks/README.md | 2 +-
.../todos/cqrs-rewrite-migration-tracking.md | 24 ++++-
.../traces/cqrs-rewrite-migration-trace.md | 30 ++++++
6 files changed, 205 insertions(+), 9 deletions(-)
create mode 100644 GFramework.Cqrs.Benchmarks/Messaging/GeneratedDefaultRequestBenchmarkRegistry.cs
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs b/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs
index 886f2c71..a872b5a8 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs
@@ -3,10 +3,12 @@
using System;
using System.Linq;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Ioc;
using GFramework.Cqrs.Abstractions.Cqrs;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
+using LegacyICqrsRuntime = GFramework.Core.Abstractions.Cqrs.ICqrsRuntime;
namespace GFramework.Cqrs.Benchmarks.Messaging;
@@ -31,11 +33,54 @@ internal static class BenchmarkHostFactory
ArgumentNullException.ThrowIfNull(configure);
var container = new MicrosoftDiContainer();
+ RegisterCqrsInfrastructure(container);
configure(container);
container.Freeze();
return container;
}
+ ///
+ /// 为 benchmark 宿主补齐默认 CQRS runtime seam,确保它既能手工注册 handler,也能走真实的程序集注册入口。
+ ///
+ /// 当前 benchmark 拥有的 GFramework 容器。
+ ///
+ /// `RegisterCqrsHandlersFromAssembly(...)` 依赖预先可见的 runtime / registrar / registration service 实例绑定。
+ /// benchmark 宿主直接使用裸 ,因此需要在配置阶段先补齐这组基础设施,
+ /// 避免各个 benchmark 用例各自复制同一段前置接线逻辑。
+ ///
+ private static void RegisterCqrsInfrastructure(MicrosoftDiContainer container)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+
+ if (container.Get() is null)
+ {
+ var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
+ var notificationPublisher = container.Get();
+ var runtime = GFramework.Cqrs.CqrsRuntimeFactory.CreateRuntime(container, runtimeLogger, notificationPublisher);
+ container.Register(runtime);
+ container.Register((LegacyICqrsRuntime)runtime);
+ }
+ else if (container.Get() is null)
+ {
+ container.Register((LegacyICqrsRuntime)container.GetRequired());
+ }
+
+ if (container.Get() is null)
+ {
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
+ var registrar = GFramework.Cqrs.CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger);
+ container.Register(registrar);
+ }
+
+ if (container.Get() is null)
+ {
+ var registrationLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsRegistrationService");
+ var registrar = container.GetRequired();
+ var registrationService = GFramework.Cqrs.CqrsRuntimeFactory.CreateRegistrationService(registrar, registrationLogger);
+ container.Register(registrationService);
+ }
+ }
+
///
/// 创建只承载当前 benchmark handler 集合的最小 MediatR 宿主。
///
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/GeneratedDefaultRequestBenchmarkRegistry.cs b/GFramework.Cqrs.Benchmarks/Messaging/GeneratedDefaultRequestBenchmarkRegistry.cs
new file mode 100644
index 00000000..59646dc1
--- /dev/null
+++ b/GFramework.Cqrs.Benchmarks/Messaging/GeneratedDefaultRequestBenchmarkRegistry.cs
@@ -0,0 +1,100 @@
+// Copyright (c) 2025-2026 GeWuYou
+// SPDX-License-Identifier: Apache-2.0
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using Microsoft.Extensions.DependencyInjection;
+
+[assembly: GFramework.Cqrs.CqrsHandlerRegistryAttribute(
+ typeof(GFramework.Cqrs.Benchmarks.Messaging.GeneratedDefaultRequestBenchmarkRegistry))]
+
+namespace GFramework.Cqrs.Benchmarks.Messaging;
+
+///
+/// 为默认 request steady-state benchmark 提供 hand-written generated registry,
+/// 以便验证“默认宿主吸收 generated request invoker provider”后的热路径收益。
+///
+public sealed class GeneratedDefaultRequestBenchmarkRegistry :
+ GFramework.Cqrs.ICqrsHandlerRegistry,
+ GFramework.Cqrs.ICqrsRequestInvokerProvider,
+ GFramework.Cqrs.IEnumeratesCqrsRequestInvokerDescriptors
+{
+ private static readonly GFramework.Cqrs.CqrsRequestInvokerDescriptor Descriptor =
+ new(
+ typeof(IRequestHandler<
+ RequestBenchmarks.BenchmarkRequest,
+ RequestBenchmarks.BenchmarkResponse>),
+ typeof(GeneratedDefaultRequestBenchmarkRegistry).GetMethod(
+ nameof(InvokeBenchmarkRequestHandler),
+ BindingFlags.Public | BindingFlags.Static)
+ ?? throw new InvalidOperationException("Missing generated default request benchmark method."));
+
+ private static readonly IReadOnlyList Descriptors =
+ [
+ new GFramework.Cqrs.CqrsRequestInvokerDescriptorEntry(
+ typeof(RequestBenchmarks.BenchmarkRequest),
+ typeof(RequestBenchmarks.BenchmarkResponse),
+ Descriptor)
+ ];
+
+ ///
+ /// 把默认 request benchmark handler 注册为单例,保持与原先 steady-state 宿主一致的生命周期语义。
+ ///
+ public void Register(IServiceCollection services, ILogger logger)
+ {
+ ArgumentNullException.ThrowIfNull(services);
+ ArgumentNullException.ThrowIfNull(logger);
+
+ services.AddSingleton(
+ typeof(IRequestHandler),
+ typeof(RequestBenchmarks.BenchmarkRequestHandler));
+ logger.Debug("Registered generated default request benchmark handler.");
+ }
+
+ ///
+ /// 返回当前 provider 暴露的全部 generated request invoker 描述符。
+ ///
+ public IReadOnlyList GetDescriptors()
+ {
+ return Descriptors;
+ }
+
+ ///
+ /// 为目标请求/响应类型对返回 generated request invoker 描述符。
+ ///
+ public bool TryGetDescriptor(
+ Type requestType,
+ Type responseType,
+ out GFramework.Cqrs.CqrsRequestInvokerDescriptor? descriptor)
+ {
+ if (requestType == typeof(RequestBenchmarks.BenchmarkRequest) &&
+ responseType == typeof(RequestBenchmarks.BenchmarkResponse))
+ {
+ descriptor = Descriptor;
+ return true;
+ }
+
+ descriptor = null;
+ return false;
+ }
+
+ ///
+ /// 模拟 generated invoker provider 为默认 request benchmark 产出的开放静态调用入口。
+ ///
+ public static ValueTask InvokeBenchmarkRequestHandler(
+ object handler,
+ object request,
+ CancellationToken cancellationToken)
+ {
+ var typedHandler = (IRequestHandler<
+ RequestBenchmarks.BenchmarkRequest,
+ RequestBenchmarks.BenchmarkResponse>)handler;
+ var typedRequest = (RequestBenchmarks.BenchmarkRequest)request;
+ return typedHandler.Handle(typedRequest, cancellationToken);
+ }
+}
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
index 37840a94..a84c6d1a 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
@@ -61,12 +61,12 @@ public class RequestBenchmarks
MinLevel = LogLevel.Fatal
};
Fixture.Setup("Request", handlerCount: 1, pipelineCount: 0);
+ BenchmarkDispatcherCacheHelper.ClearDispatcherCaches();
_baselineHandler = new BenchmarkRequestHandler();
_container = BenchmarkHostFactory.CreateFrozenGFrameworkContainer(container =>
{
- container.RegisterSingleton>(
- _baselineHandler);
+ container.RegisterCqrsHandlersFromAssembly(typeof(RequestBenchmarks).Assembly);
});
_runtime = GFramework.Cqrs.CqrsRuntimeFactory.CreateRuntime(
_container,
@@ -91,7 +91,14 @@ public class RequestBenchmarks
[GlobalCleanup]
public void Cleanup()
{
- BenchmarkCleanupHelper.DisposeAll(_container, _mediatrServiceProvider, _mediatorServiceProvider);
+ try
+ {
+ BenchmarkCleanupHelper.DisposeAll(_container, _mediatrServiceProvider, _mediatorServiceProvider);
+ }
+ finally
+ {
+ BenchmarkDispatcherCacheHelper.ClearDispatcherCaches();
+ }
}
///
diff --git a/GFramework.Cqrs.Benchmarks/README.md b/GFramework.Cqrs.Benchmarks/README.md
index 9fc28cbd..20558821 100644
--- a/GFramework.Cqrs.Benchmarks/README.md
+++ b/GFramework.Cqrs.Benchmarks/README.md
@@ -15,7 +15,7 @@
- `Messaging/Fixture.cs`
- 运行前输出并校验场景配置
- `Messaging/RequestBenchmarks.cs`
- - direct handler、NuGet `Mediator` source-generated concrete path、`GFramework.Cqrs` runtime 与 `MediatR` 的 request steady-state dispatch 对比
+ - direct handler、NuGet `Mediator` source-generated concrete path、已接上 handwritten generated request invoker provider 的默认 `GFramework.Cqrs` runtime 与 `MediatR` 的 request steady-state dispatch 对比
- `Messaging/RequestLifetimeBenchmarks.cs`
- `Singleton / Transient` 两类 handler 生命周期下,direct handler、`GFramework.Cqrs` runtime 与 `MediatR` 的 request steady-state dispatch 对比
- `Messaging/RequestPipelineBenchmarks.cs`
diff --git a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
index 6ada6984..01865d69 100644
--- a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
+++ b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
@@ -7,7 +7,7 @@ CQRS 迁移与收敛。
## 当前恢复点
-- 恢复点编号:`CQRS-REWRITE-RP-104`
+- 恢复点编号:`CQRS-REWRITE-RP-105`
- 当前阶段:`Phase 8`
- 当前 PR 锚点:`PR #340`
- 当前结论:
@@ -41,18 +41,19 @@ CQRS 迁移与收敛。
- 当前 `RP-102` 已将 `BenchmarkDotNet.Artifacts/` 收口为默认忽略路径,并把 request steady-state / lifetime benchmark 复跑升级为 CQRS 性能相关改动的默认回归门槛;当前阶段目标明确为“持续逼近 source-generated `Mediator`,并至少稳定超过反射版 `MediatR`”
- 当前 `RP-103` 已使用 `$gframework-pr-review` 复核 `PR #340` latest-head review:修复 `CreateStream_Should_Throw_When_Stream_Pipeline_Behavior_Context_Does_Not_Implement_IArchitectureContext` 因 strict mock 未配置 `HasRegistration(Type)` 产生的 CI 失败,收紧 `MicrosoftDiContainer.HasRegistration(Type)` 到与 `GetAll(Type)` 一致的服务键可见性语义,补齐 `IIocContainer.HasRegistration(Type)` 的异常/XML 契约与 `docs/zh-CN/core/ioc.md` 的用户接入说明,并同步 benchmark 注释与 active tracking/trace 到当前 PR 锚点
- 当前 `RP-104` 已继续沿用 `$gframework-batch-boot 50` 压 request 热路径:先把 `CqrsDispatcher.SendAsync(...)` 改成 direct-return `ValueTask`,移除 dispatcher 自身的 `async/await` 状态机;再让 `MicrosoftDiContainer.HasRegistration(Type)` 在冻结后复用预构建的服务键索引,避免每次命中零 pipeline request 都线性扫描全部描述符;本轮 benchmark 表明第一刀显著压低 steady-state / lifetime request,第二刀在当前短跑下主要确认“无回退、收益不明显”
-- `ai-plan` active 入口现以 `RP-104` 为最新恢复锚点;`PR #340`、`PR #339`、`PR #334`、`PR #331`、`PR #326`、`PR #323`、`PR #307` 与其他更早阶段细节均以下方归档或说明为准
+ - 当前 `RP-105` 已继续沿用 `$gframework-batch-boot 50` 压默认 request steady-state:为 benchmark 最小宿主补齐 CQRS runtime / registrar / registration service 基础设施,让 `RequestBenchmarks` 不再只测反射路径,而是通过 handwritten generated registry + `RegisterCqrsHandlersFromAssembly(...)` 真实接上 generated request invoker provider;本轮 benchmark 表明默认 request 路径进一步从约 `70.298 ns / 32 B` 压到约 `65.296 ns / 32 B`,`Singleton / Transient` lifetime 也同步收敛到约 `68.772 ns / 32 B` 与 `73.157 ns / 56 B`
+- `ai-plan` active 入口现以 `RP-105` 为最新恢复锚点;`PR #340`、`PR #339`、`PR #334`、`PR #331`、`PR #326`、`PR #323`、`PR #307` 与其他更早阶段细节均以下方归档或说明为准
## 当前活跃事实
- 当前分支为 `feat/cqrs-optimization`
- 本轮 `$gframework-batch-boot 50` 以 `origin/main` (`4d6dbba6`, 2026-05-08 11:13:33 +0800) 为基线;本地 `main` 仍落后,不作为 branch diff 基线
-- 当前分支相对 `origin/main` 的累计 branch diff 仍为 `0 files / 0 lines`;本轮待提交工作树目前集中在 `GFramework.Cqrs/Internal/CqrsDispatcher.cs`、`GFramework.Core/Ioc/MicrosoftDiContainer.cs` 与 active tracking/trace,仍明显低于 `$gframework-batch-boot 50` 的文件阈值
+- 当前已提交分支相对 `origin/main` 的累计 branch diff 为 `4 files / 154 lines`;本批待提交工作树额外集中在 `GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs`、`GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs`、`GFramework.Cqrs.Benchmarks/Messaging/GeneratedDefaultRequestBenchmarkRegistry.cs` 与 `GFramework.Cqrs.Benchmarks/README.md`,约 `4 files / 160 changed lines`,合并后仍明显低于 `$gframework-batch-boot 50` 的文件阈值
- `GFramework.Cqrs.Benchmarks` 作为 benchmark 基础设施项目,必须持续排除在 NuGet / GitHub Packages 发布集合之外
- `GFramework.Cqrs.Benchmarks` 现已覆盖 request steady-state、pipeline 数量矩阵、startup、request/stream generated invoker,以及 request handler `Singleton / Transient` 生命周期矩阵
- `GFramework.Cqrs.Benchmarks` 当前以 NuGet 方式引用 `Mediator.Abstractions` / `Mediator.SourceGenerator` `3.0.2`;`ai-libs/Mediator` 只保留为本地源码/README 对照资料,不再参与 benchmark 项目编译
-- 当前 request steady-state benchmark 已形成 baseline / `Mediator` / `MediatR` / `GFramework.Cqrs` 四方对照:最新约 `6.141 ns / 32 B`、`6.674 ns / 32 B`、`61.803 ns / 232 B`、`70.298 ns / 32 B`
-- 当前 request lifetime benchmark 已继续收敛:`Singleton` 下 `GFramework.Cqrs` 最新约 `73.005 ns / 32 B`,`Transient` 下约 `74.757 ns / 56 B`;相较 `RP-103` 前的 `83.183 ns / 32 B` 与 `86.243 ns / 56 B` 已继续下降
+- 当前 request steady-state benchmark 已形成 baseline / `Mediator` / `MediatR` / `GFramework.Cqrs` 四方对照:最新约 `5.013 ns / 32 B`、`5.747 ns / 32 B`、`51.588 ns / 232 B`、`65.296 ns / 32 B`
+- 当前 request lifetime benchmark 已继续收敛:`Singleton` 下 `GFramework.Cqrs` 最新约 `68.772 ns / 32 B`,`Transient` 下约 `73.157 ns / 56 B`;相较 `RP-104` 前的 `73.005 ns / 32 B` 与 `74.757 ns / 56 B` 已继续下降
- 本轮已验证旧 benchmark 劣化的两个主热点:`0 pipeline` 场景下仍解析空行为列表,以及容器查询热路径在 debug 禁用时仍构造日志字符串;两者收口后,`GFramework.Cqrs` request 路径不再出现额外数百字节分配
- `HasRegistration(Type)` 现在只把“同一服务键已注册”或“开放泛型服务键可闭合到目标类型”视为命中,不再把“仅以具体实现类型自注册”的行为误判为接口服务已注册;该语义与 `Get(Type)` / `GetAll(Type)` 已重新对齐
- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs` 已同步适配 `HasRegistration(Type)` fast-path,避免 strict mock 因缺少新调用配置而在上下文失败语义断言前提前抛出 `Moq.MockException`
@@ -60,6 +61,7 @@ CQRS 迁移与收敛。
- 当前 request steady-state 仍落后于 source-generated `Mediator` 与 `MediatR`,但差距已从“额外数百字节分配 + 近 300ns”收敛到“零 pipeline fast-path 仍慢约 `31ns` / `3.6x` 于 `Mediator`”;下一批若继续压 request dispatch,应优先评估默认路径吸收 generated invoker/provider 的空间
- 本轮 `SendAsync(...)` 的 direct-return `ValueTask` 改动已证明确实是有效热点:同样的短跑配置下,`GFramework.Cqrs` steady-state request 从约 `83.823 ns` 下探到 `69-70 ns` 区间
- 冻结后 `HasRegistration(Type)` 服务键索引化在当前短跑下没有带来同等量级的可见收益,但也没有引入功能回退或额外分配;后续若继续压零 pipeline request,应优先重新评估“默认 request 路径进一步吸收 generated invoker/provider”而不是继续堆叠同层级微优化
+- 默认 `RequestBenchmarks` 现在已通过 handwritten generated registry + 真实 `RegisterCqrsHandlersFromAssembly(...)` 宿主接线命中 generated request invoker provider,不再只代表纯反射 request binding 路径
- 当前性能回归门槛已收紧为:只要改动触达 `GFramework.Cqrs` request dispatch、DI 热路径、invoker/provider、pipeline 或 benchmark 宿主,就必须至少复跑 `RequestBenchmarks.SendRequest_*` 与 `RequestLifetimeBenchmarks.SendRequest_*`
- 当前阶段的性能验收目标已明确为:默认 request steady-state 路径不要求超过 source-generated `Mediator`,但必须持续逼近它,并至少稳定快于基于反射 / 扫描的 `MediatR`
- `GFramework.Core` 当前已通过内部 bridge request / handler 把 legacy `ICommand`、`IAsyncCommand`、`IQuery`、`IAsyncQuery` 接到统一 `ICqrsRuntime`
@@ -155,6 +157,18 @@ CQRS 迁移与收敛。
- `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --filter "*RequestLifetimeBenchmarks.SendRequest_*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
- 结果:通过
- 备注:最新 lifetime request 对照约为 `Singleton` 下 baseline / `MediatR` / `GFramework.Cqrs` = `4.706 ns / 52.197 ns / 73.005 ns`,`Transient` 下 = `4.571 ns / 50.175 ns / 74.757 ns`
+- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+- `python3 scripts/license-header.py --check --paths GFramework.Cqrs.Benchmarks/Messaging/GeneratedDefaultRequestBenchmarkRegistry.cs GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs GFramework.Cqrs.Benchmarks/README.md`
+ - 结果:通过
+- `git diff --check`
+ - 结果:通过
+- `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release -- --filter "*RequestBenchmarks.SendRequest_*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - 结果:通过
+ - 备注:默认 steady-state request 对照现约为 baseline `5.013 ns / 32 B`、`Mediator` `5.747 ns / 32 B`、`MediatR` `51.588 ns / 232 B`、`GFramework.Cqrs` `65.296 ns / 32 B`
+- `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release -- --filter "*RequestLifetimeBenchmarks.SendRequest_*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - 结果:通过
+ - 备注:最新 lifetime request 对照约为 `Singleton` 下 baseline / `MediatR` / `GFramework.Cqrs` = `4.817 ns / 48.177 ns / 68.772 ns`,`Transient` 下 = `4.841 ns / 51.753 ns / 73.157 ns`
- `env GIT_DIR=... GIT_WORK_TREE=... python3 scripts/license-header.py --check`
- 结果:通过
- `git diff --check`
diff --git a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
index 002ee574..bdf12ccc 100644
--- a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
+++ b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
@@ -2,6 +2,36 @@
## 2026-05-08
+### 阶段:默认 request benchmark 吸收 generated provider 宿主(CQRS-REWRITE-RP-105)
+
+- 延续 `$gframework-batch-boot 50`,本轮先确认失败试验已手工回退回 `RP-104` 的已验证状态,再重新评估“默认 request 路径继续逼近 source-generated `Mediator`”的下一刀
+- 本轮接受的只读探索结论:
+ - 继续在 `CqrsDispatcher` 或 `MicrosoftDiContainer` 上堆叠同层级微优化的性价比已经下降,而且上一轮“总是 `GetAll(Type)`”的试验已被 benchmark 明确否决
+ - 默认 `RequestBenchmarks` 虽然已包含 `Mediator` 对照,但当前 GFramework 组仍只注册了单个 handler 实例,没有走 `RegisterCqrsHandlersFromAssembly(...)` + generated registry/provider 的真实宿主接线路径
+ - `RequestInvokerBenchmarks` 已证明 generated request invoker provider 路径比纯反射 binding 更接近目标,因此下一批最小切片应先把这条收益吸收到默认 steady-state request benchmark
+- 本轮主线程决策:
+ - 在 `BenchmarkHostFactory` 内补齐 benchmark 最小宿主的 CQRS 基础设施预接线:runtime、legacy alias、registrar、registration service
+ - 新增 `GeneratedDefaultRequestBenchmarkRegistry`,用 handwritten generated registry + `ICqrsRequestInvokerProvider` + `IEnumeratesCqrsRequestInvokerDescriptors` 为 `RequestBenchmarks.BenchmarkRequest` 提供真实的 generated request invoker descriptor
+ - 让 `RequestBenchmarks` 改用 `RegisterCqrsHandlersFromAssembly(typeof(RequestBenchmarks).Assembly)` 建容器,并在 `Setup/Cleanup` 前后显式清理 dispatcher 静态缓存,避免前一组 benchmark 污染默认 request steady-state 结果
+- 本轮权威验证:
+ - `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+ - `python3 scripts/license-header.py --check --paths GFramework.Cqrs.Benchmarks/Messaging/GeneratedDefaultRequestBenchmarkRegistry.cs GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs GFramework.Cqrs.Benchmarks/README.md`
+ - 结果:通过
+ - `git diff --check`
+ - 结果:通过
+ - `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release -- --filter "*RequestBenchmarks.SendRequest_*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - 结果:通过
+ - 备注:steady-state request 对照约为 baseline `5.013 ns / 32 B`、`Mediator` `5.747 ns / 32 B`、`MediatR` `51.588 ns / 232 B`、`GFramework.Cqrs` `65.296 ns / 32 B`
+ - `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release -- --filter "*RequestLifetimeBenchmarks.SendRequest_*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - 结果:通过
+ - 备注:`Singleton` 下 `GFramework.Cqrs` / `MediatR` 约 `68.772 ns / 32 B` vs `48.177 ns / 232 B`;`Transient` 下约 `73.157 ns / 56 B` vs `51.753 ns / 232 B`
+- 本轮结论:
+ - 默认 request benchmark 现在终于测到了“默认宿主已吸收 generated request invoker provider”后的真实 steady-state,而不再只是纯反射 request binding
+ - 这条宿主层收口在不改 runtime 语义的前提下,把 `GFramework.Cqrs` steady-state request 从约 `70.298 ns` 再压到约 `65.296 ns`
+ - lifetime 矩阵也同步改善到 `68.772 ns / 73.157 ns`,说明默认 request 宿主吸收 generated provider 不只是 benchmark 口径变化,而是对常见 handler 生命周期也有稳定收益
+ - 下一批若继续沿用 `$gframework-batch-boot 50`,应优先转向 pipeline 路径或 handler 解析热路径中仍未吸收 generated/provider 收益的常量开销,而不是回头重试已被否决的 `GetAll(Type)` 零行为探测方案
+
### 阶段:request 热路径继续收口(CQRS-REWRITE-RP-104)
- 延续 `$gframework-batch-boot 50`,本轮先重新按 `origin/main` 复核 branch diff 基线: