即时通讯后端【免费下载链接】WuKongIMMore than just IM 不只是即时通讯(IM)项目地址https://gitcode.com/gh_mirrors/wu/WuKongIM点击查看免费下载本文整理 WuKongIM 仓库内部维护的插件plugin迁移微基准基线文档 PLUGIN_BENCHMARK_BASELINE.md。读完后你将掌握三件事如何在改动插件分发、host RPC 映射、PersistAfter/Send/Receive 钩子、NoPersist 实时投递或插件指标之前用仓库给定的go test -bench命令复现同一套基准如何在 Apple M4、go1.25.0 环境下记录的一组完整基准数据作为回归参照以及如何按仓库明确的规则区分“真实的内存/分配退化”与“单次计时噪声”避免误报性能回归。一、基线文档的适用范围基线文档开头明确了它的使用时机在改动触及以下任何路径之前和之后都应运行并对照这组基准插件分发plugin dispatchhost RPC 映射插件宿主侧的 RPC 转发PersistAfter 钩子消息落库提交后触发Send 钩子与 Receive 钩子NoPersist 实时投递不落盘的一次性实时消息离线接收者offline recipient探测插件指标plugin metrics。从源码结构看这七个范围恰好对应仓库中四组独立的基准测试文件文档把它们统一纳入同一套回归参照体系基线覆盖范围对应基准测试文件PersistAfter 映射、Send 钩子、host RPC 映射、候选集选择internal/usecase/plugin/benchmark_test.go异步钩子队列的入队/队列打满行为internal/runtime/pluginhook/benchmark_test.go插件事件的深拷贝成本internal/contracts/pluginevents/benchmark_test.goNoPersist 实时投递与提交后插件入队开销internal/runtime/channelappend/benchmark_test.go大接收者规模下的在线投递离线分类internal/runtime/delivery/runtime_benchmark_test.go指标观察者Observer本身的开销internal/app/plugin_observer_benchmark_test.go二、基线环境Commit、机器与 Go 版本文档记录基准来自以下环境这是判断数据可比性的前提基线 Commitc6e42412a9ce test: add plugin benchmark baselinesReceive hook 扩展数据记录自 2026-06-22 工作区working tree时间点位于 Receive hook 提交之前属于同一天的补充采样记录日期2026-06-22硬件平台Apple M4darwin/arm64Go 版本go1.25.0。当前仓库 go.mod 声明go 1.25.0toolchaingo1.25.11与基线记录一致。需要特别注意这组 ns/op 数字只在同一台 M4 机器上具有直接可比性。跨机器、跨架构例如 x86_64 服务器对比时只能把基线当作量级参照回归结论必须在改动前后的同一台机器上重新测量。这一点与文档“Interpreting Regressions”一节的规则互为呼应。三、基准命令如何完整复现文档给出三组命令覆盖了全部六个基准文件。所有命令都使用-run ^$不跑普通测试、-benchmem报告分配数据和-benchtime3s每个基准运行 3 秒以保证统计稳定性。3.1 插件包主组go test ./internal/usecase/plugin ./internal/runtime/pluginhook ./internal/contracts/pluginevents ./internal/app -run ^$ -bench Benchmark(PersistAfter|Receive|SendMessageFromPluginReq|ChannelMessagesFromPluginReq|ClusterConfigFromSnapshot|ClusterChannelsBelongNode|ConversationChannels|HTTPForward|ListPlugins|SendPluginCandidates|BeforeSend|PluginHook|PluginMetricsObserver) -benchmem -benchtime3s注意-bench参数使用的是交替正则PersistAfter会同时匹配BenchmarkPersistAfterMessageBatchMapping、BenchmarkPersistAfterCandidates、BenchmarkPersistAfterCommittedCloneReceive会匹配BenchmarkReceiveOffline、BenchmarkReceivePluginCandidates、BenchmarkReceiveOfflineClone、BenchmarkPluginHookReceiveEnqueue等。这正是文档把多个包合并进一条命令执行的原因——插件链路上的每个环节都有对应前缀。3.2 channelappend 插件相关子集go test ./internal/runtime/channelappend -run ^$ -bench Benchmark(SubmitLocalNoPersistRealtimeScoped|ChannelAppendPostCommitPlugin)$ -benchmem -benchtime3s注意这里用了$锚定精确匹配说明 channelappend 包内其他更大的压测如多频道并行提交不属于本基线范围刻意排除以保持回归信号聚焦。3.3 大接收者 Online Delivery 规范负载go test ./internal/runtime/delivery -run ^$ -bench ^BenchmarkRuntimeBounded100KRecipientWorkload$ -benchmem -benchtime3s这是文档指定的 canonical规范大接收者工作负载。后文会解释为什么大群回归覆盖必须集中在这一个基准上而不能另起炉灶。四、基线数据2026-06-22Apple M4darwin/arm64go1.25.0以下数据完整继承自 基线文档是改动前后对照的参照值。4.1 规范 Online Delivery 离线分类Benchmarkns/opB/opallocs/opExtraBenchmarkRuntimeBounded100KRecipientWorkload2714780110107715865100000 offline_recipients/workload这个单次“op”处理 10 万个离线接收者39 个投递 plan每 plan 256 人全部经 presence 解析后被判定为离线并走OfflineRecipientsObserver路径。Extra列的100000 offline_recipients/workload由b.ReportMetric输出直接验证了离线分类结果的数量正确性见 runtime_benchmark_test.go#L113-L168。4.2 Plugin usecaseBenchmarkns/opB/opallocs/opBenchmarkPersistAfterMessageBatchMapping/payload_12871.603924BenchmarkPersistAfterMessageBatchMapping/payload_1024155.112884BenchmarkPersistAfterMessageBatchMapping/payload_163841528166484BenchmarkSendMessageFromPluginReq/payload_12856.761762BenchmarkSendMessageFromPluginReq/payload_1024137.110722BenchmarkSendMessageFromPluginReq/payload_163841401164322BenchmarkChannelMessagesFromPluginReq/items_1133.65288BenchmarkChannelMessagesFromPluginReq/items_161802748898BenchmarkChannelMessagesFromPluginReq/items_1281425959584770BenchmarkClusterConfigFromSnapshot974247384526BenchmarkClusterChannelsBelongNode/items_1136.72567BenchmarkClusterChannelsBelongNode/items_16819.2179236BenchmarkClusterChannelsBelongNode/items_128404711648157BenchmarkConversationChannels/items_139.641363BenchmarkConversationChannels/items_16280.7121618BenchmarkConversationChannels/items_12820279408130BenchmarkConversationChannels/items_100016144722561002BenchmarkHTTPForward/local_payload_1283149418458BenchmarkHTTPForward/remote_payload_128328.27366BenchmarkHTTPForward/local_payload_10243234784858BenchmarkHTTPForward/remote_payload_1024371.516326BenchmarkHTTPForward/local_payload_1638489367120858BenchmarkHTTPForward/remote_payload_163841752169926BenchmarkHTTPForwardFanoutDeferred68.422003BenchmarkPersistAfterCandidates/plugins_1113.93604BenchmarkPersistAfterCandidates/plugins_161356586421BenchmarkPersistAfterCandidates/plugins_1281424845800133BenchmarkPersistAfterCandidates/plugins_10241352533443061029BenchmarkReceivePluginCandidates/bindings_1233.45205BenchmarkReceivePluginCandidates/bindings_163076948825BenchmarkReceivePluginCandidates/bindings_1282663674128137BenchmarkReceivePluginCandidates/bindings_10242504135627451035BenchmarkReceiveOffline/payload_128705.998311BenchmarkReceiveOffline/payload_10241013287111BenchmarkReceiveOffline/payload_1638460883552711BenchmarkListPlugins/plugins_185.363363BenchmarkListPlugins/plugins_16858.1563218BenchmarkListPlugins/plugins_2561170886017258BenchmarkListPlugins/plugins_1024502083440681026BenchmarkSendPluginCandidates/plugins_1107.23604BenchmarkSendPluginCandidates/plugins_161339586421BenchmarkSendPluginCandidates/plugins_2562809386249261BenchmarkSendPluginCandidates/plugins_10241292683443061029BenchmarkBeforeSend/no_candidates50.76241BenchmarkBeforeSend/one_plugin419.076811BenchmarkBeforeSend/chain_41569338437几个值得注意的读数规律映射类基准的分配几乎与 payload 大小无关PersistAfterMessageBatchMapping在 128 B 到 16 KB 三档 payload 下都是 4 次分配说明映射路径对 payload 只做切片搬运而不做深拷贝拷贝成本单独由合同层 Clone 基准度量见 4.4 节候选集基准呈近似线性增长plugins_1→plugins_1024ns/op 从约 114 增长到约 135 000allocs/op 从 4 增长到 1029约对应每个插件 1 次分配——这是评估“插件注册表/选择逻辑”改动的核心信号HTTP 转发的 local 与 remote 差距local 路径经 Invoker 真正走 HTTP 路由固定约 58 次分配而 remote 路径转发到远端节点恒定 6 次分配两者成本结构完全不同回归时要分开看。4.3 Plugin hook runtimeBenchmarkns/opB/opallocs/opBenchmarkPluginHookEnqueue799.913315BenchmarkPluginHookQueueFull22634972574BenchmarkPluginHookReceiveEnqueue106013525BenchmarkPluginHookReceiveQueueFull222282225744.4 Plugin event contractsBenchmarkns/opB/opallocs/opBenchmarkPersistAfterCommittedClone/payload_128/scoped_022.881281BenchmarkPersistAfterCommittedClone/payload_128/scoped_1051.562882BenchmarkPersistAfterCommittedClone/payload_128/scoped_10001243165122BenchmarkPersistAfterCommittedClone/payload_1024/scoped_087.1810241BenchmarkPersistAfterCommittedClone/payload_1024/scoped_10109.411842BenchmarkPersistAfterCommittedClone/payload_1024/scoped_10001182174082BenchmarkPersistAfterCommittedClone/payload_16384/scoped_01045163841BenchmarkPersistAfterCommittedClone/payload_16384/scoped_101073165442BenchmarkPersistAfterCommittedClone/payload_16384/scoped_10002190327682BenchmarkReceiveOfflineClone/payload_128/scoped_033.251281BenchmarkReceiveOfflineClone/payload_128/scoped_1076.762882BenchmarkReceiveOfflineClone/payload_128/scoped_10001903165122BenchmarkReceiveOfflineClone/payload_1024/scoped_0147.410241BenchmarkReceiveOfflineClone/payload_1024/scoped_10195.811842BenchmarkReceiveOfflineClone/payload_1024/scoped_10001925174082BenchmarkReceiveOfflineClone/payload_16384/scoped_01775163841BenchmarkReceiveOfflineClone/payload_16384/scoped_101750165442BenchmarkReceiveOfflineClone/payload_16384/scoped_100038803276824.5 App 插件指标观察者Benchmarkns/opB/opallocs/opBenchmarkPluginMetricsObserverSendInvoke117.81284BenchmarkPluginMetricsObserverReceiveEnqueue168.61284BenchmarkPluginMetricsObserverReceiveInvoke161.712844.6 Channelappend 插件与 NoPersist 边界Benchmarkns/opB/opallocs/opExtraBenchmarkSubmitLocalNoPersistRealtimeScoped33015185433.000 goroutine-deltaBenchmarkChannelAppendPostCommitPlugin/disabled101.63201BenchmarkChannelAppendPostCommitPlugin/enabled_enqueue105.13201goroutine-delta是b.ReportMetric输出的 goroutine 数量差3.000 说明该基准路径没有 goroutine 泄漏disabled与enabled_enqueue的 ns/op 几乎持平101.6 vs 105.1说明提交后插件入队的固定开销非常小。五、源码级解读这些数字从哪来5.1 usecase 层映射、发送与钩子候选集benchmark_test.go 中的基准通过NewApp(Options{...})注入recordingRuntime、recordingInvoker等 fake 端口把测量焦点收敛到 usecase 层自身的逻辑。以BenchmarkSendMessageFromPluginReq第 46-81 行为例它构造带NoPersist/SyncOnce/RedDot头的SendReq每轮调用app.SendMessage并校验返回的MessageId非零——因此测的是“插件发起一条消息”的完整 usecase 路径而不含真实 RPC 与存储。BenchmarkBeforeSend定义了no_candidates、one_plugin、chain_4三档钩子链长度正好覆盖“没有插件参与”“单插件”“四插件链式执行”的相对成本BenchmarkHTTPForwardFanoutDeferred则专门验证ToNodeId: -1的广播转发被延迟处理并返回ErrHTTPForwardFanoutDeferred且响应为 nil第 248-271 行是 fanout 延迟语义的基准化契约。5.2 合同层事件 Clone 的深拷贝语义异步插件 worker 消费的是入队时深拷贝的事件副本PersistAfterCommitted.Clone()与ReceiveOffline.Clone()的实现types.go#L34-L38、L69-L73只复制两个可变部分e.Payload append([]byte(nil), e.Payload...) e.MessageScopedUIDs append([]string(nil), e.MessageScopedUIDs...)合同基准用payload大小128/1024/16384×MessageScopedUIDs数量0/10/1000九宫格展开正是为了分别暴露“payload 拷贝”和“scoped UID 切片拷贝”两个成本维度scoped_0时恒定 1 次分配仅 payloadscoped_10/scoped_1000时恒定 2 次分配。这也解释了为什么 usecase 层映射基准的 allocs/op 与 payload 大小无关——深拷贝被有意推到了入队边界。5.3 pluginhook 运行时有界准入、fail-open 与“队列打满”基准worker.go 实现了有界插件钩子 worker关键常量第 14-26 行defaultQueueSize 1024 defaultWorkers 1 defaultInvokeTimeout time.Second enqueueAdmissionTimeout 2 * time.MillisecondEnqueuePersistAfter/EnqueueReceive/EnqueueReceiveBatch三个入口在入队前统一调用event.Clone()第 237-250 行入队采用两段式先尝试无锁快路径tryEnqueueFast队列满时进入带 2ms 超时器的select超时即记为full并放弃该事件绝不阻塞或拖垮调用方第 252-287 行。这就是 4.3 节两组 QueueFull 基准的由来benchmark_test.go#L96-L124 用QueueSize: 1, Workers: 1加一个阻塞至 context 结束的 usecase 制造“队列永久打满”场景ns/op ≈ 2.26ms恰好对应enqueueAdmissionTimeout的 2ms 上限加少量开销。换句话说这组基线锁定的是一项可用性契约插件侧再慢消息提交路径的入队成本也不会超过约 2ms代价是事件按 fail-open 语义丢弃。任何改动若让 QueueFull 的 ns/op 显著偏离 2ms 量级或让 Enqueue 的 allocs/op 上升当前 PersistAfter/Receive 各 5 次都应按回归处理。此外worker 的Observer接口第 57-63 行记录每次入队与调用的结果和耗时其真实实现pluginHookMetricsObserver的成本由 4.5 节三组观察者基准锁定——指标上报路径本身约 120~170ns、恒定 4 次分配不会反向成为热点。5.4 delivery 规范负载10 万离线接收者分类BenchmarkRuntimeBounded100KRecipientWorkload 构造 39 个 plan每 plan 256 个接收者共 100 000 个 UIDpresence 解析器统一返回空结果即全部离线再由benchmarkOfflineRecipientsObserver累计接收者与回调次数并在结尾断言b.N * 100000的精确数量。基准还通过b.ReportMetric输出recipients/workload、offline_recipients/workload、recipient_bytes/workload三项自定义指标。文档特别规定大接收者的 Online Delivery 回归覆盖归属于这一个基准不要在 channelappend 里另建 recipient-processor 基准——从源码看channelappend 侧已有BenchmarkVeryLargeGroupRecipientDispatch覆盖“分页→权威分组→投递计划准入”的上游段两者分段协作而不重叠各自负责自己边界上的 CPU 与分配成本。5.5 channelappendNoPersist 实时投递与提交后插件BenchmarkSubmitLocalNoPersistRealtimeScopedbenchmark_test.go#L147-L177构造NoPersisttrue、SyncOncetrue且带MessageScopedUIDs: [u1]的实时消息经OnlineDeliveryEnqueuer计数型 fake 完成“提交→实时投递计划入队”的闭环并在计时结束后校验 goroutine 差。文档明确要求该基准必须保留在 channelappend 子集中因为命令式 NoPersist 实时投递的职责归属在 channelappend而不是 plugin usecase。BenchmarkChannelAppendPostCommitPlugin的 disabled/enabled 两档则量化了“提交效果中挂载插件入队端口”的边际成本约 3.5ns为“提交路径接入插件钩子”这一迁移决策提供了成本证据。六、回归判定规则文档原文的完整继承基线文档给出的判定规则是这份数据的“使用说明”应逐条遵循以 B/op 与 allocs/op 的持续增长作为强于单次 ns/op 变慢的信号。计时样本受机器状态影响大分配模式退化则是结构性的宣布计时回归之前必须在同一台机器上重跑同一条命令排除偶发干扰候选集与列表类基准刻意包含plugins_1024修改插件注册表或选择逻辑时不要删除该用例——它是最容易暴露每插件固定开销分配、字符串拼接的规模档Receive 候选基准刻意包含bindings_1024修改 UID 绑定选择逻辑时保留该用例大接收者回归覆盖只归属于BenchmarkRuntimeBounded100KRecipientWorkload不要在 channelappend 重建 recipient-processor 基准BenchmarkSubmitLocalNoPersistRealtimeScoped必须留在 channelappend 子集因为命令式 NoPersist 实时投递由 channelappend 负责如果一次有意的迁移导致分配增长必须在同一提交中更新本文档并写明实测权衡。第 7 条意味着这份基线文档是“活”的任何经评审接受的性能形态变化都要把新数字与理由写回文档保持“文档数字 当前主干实测”的对应关系。七、实操建议小结改动前在当前机器上用第三节三条命令各跑一轮记录完整输出含-benchmem的 B/op、allocs/op 与 Extra 指标列作为本地基线改动后原样重跑优先对照 allocs/op 与 B/op再看 ns/op对照 基线表格 时跨机器只做量级比较同机器才下结论保留文档标记的用例plugins_1024、bindings_1024、100K 工作负载、NoPersist scoped 基准都是回归检测的“探针”删除它们等价于放弃对最坏规模档的监控有意变更更新基线文档与改动同提交合入写明新数字与权衡依据。这套“命令 数据 判定规则 用例保护条款”的组合把一个容易流于形式的性能回归流程固化成了可复制、可审计的工程契约。赞分享即时通讯后端【免费下载链接】WuKongIMMore than just IM 不只是即时通讯(IM)项目地址https://gitcode.com/gh_mirrors/wu/WuKongIM点击查看免费下载相关推荐WuKongIM pkg/client 批量发送路径性能基线分配预算、微基准命令与回归判读方法WuKongIM pkg/client 批量发送路径性能基线分配预算、微基准命令与回归判读方法 本文基于 WuKongIM 仓库的 pkg/client Be即时通讯后端react-spring 从 Yarn 迁移 pnpm迁移基线Migration Baseline方法论与基准数据实录react spring 从 Yarn 迁移 pnpm迁移基线Migration Baseline方法论与基准数据实录 本指南以 react spring前端PySpark 基准测试实战用 ASV 编写、运行与回归对比 PySpark 微基准PySpark 基准测试实战用 ASV 编写、运行与回归对比 PySpark 微基准 本指南以 Apache Spark 仓库中 python/benchma大数据数据分析批处理流处理机器学习图计算创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?