十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Colibri GLM-5.2 连续批处理(mux)解码吞吐实验全解析:方法、瓶颈与容量陷阱

Colibri GLM-5.2 连续批处理(mux)解码吞吐实验全解析:方法、瓶颈与容量陷阱 Colibri GLM-5.2 连续批处理mux解码吞吐实验全解析方法、瓶颈与容量陷阱【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri导读本文基于 docs/experiments/glm52-continuous-batching-2026-07-31.md 展开完整复现 Colibri 团队在 6×RTX 5090 主机上针对 GLM-5.2 int4 模型所做的连续批处理continuous batching解码实验。实验核心问题是现有多槽 mux 调度器能否通过在同一前向中评估多个独立 KV 槽位把聚合解码吞吐推高到单流上限之上你将从中获得三方面能力理解 mux 协议的批处理工作方式与KV_SLOTS/CTX容量决策读懂 benchmark 输出并复现同款对比掌握批处理不等于吞吐倍增的实测结论与后续 CPU 多行 INT4 专家内核的优化方向。背景GLM-5.2 的 mux 连续批处理是什么Colibri 是一个纯 C 实现、零第三方依赖的 MoE 推理引擎核心卖点是专家从磁盘流式加载、用你已有的硬件跑前沿 MoE 模型。GLM-5.2 是其支持的主力模型之一int4 量化目录/data/models/GLM-5.2-colibri-int4属于实验环境的外部路径。在 docs/serve_protocol.md 中明确记录了引擎的两套服务协议协议入口选择条件使用者mux连续批处理最多 16 个 KV 槽位run_serve_muxSERVE_BATCH1openai_server.py、coli weblegacy单槽交互式run_serveSERVE1不带SERVE_BATCHcoli chatmux 协议的核心特征是prefill 串行、decode 连续批处理每前向中每个活跃槽位贡献一行row引擎把多个独立请求的 decode 步骤合并为一次批量前向。本次实验就是要量化这条路径是否真的能带来聚合吞吐提升。实验设计Method实验在 6×RTX 5090、双路 Xeon Silver 4510、251 GiB RAM 的主机上进行关键控制变量如下模型/data/models/GLM-5.2-colibri-int4实验外部路径贪心解码DRAFT0禁用推测解码、COLI_TEMP0贪心/argmax确定性输出CUDA 通路全开dense 张量、attention、路由专家、异步专家分组全部启用——对应 docs/ENVIRONMENT.md 中的COLI_CUDA、CUDA_DENSE、COLI_CUDA_ATTN、COLI_GROUP_ASYNC等开关冻结放置frozen placement使用/data/test/colibri-simroute-20260726/prune/frozen-general.stats固定专家放置避免放置策略差异干扰对比全驻留full residency9,335 个 VRAM 专家 10,121 个 RAM 专家零磁盘读取——把 IO 因素完全排除在计算对比之外8 个 KV 槽位CTX256足够覆盖 21-token 提示词 32-token 补全同时不浪费专家驻留所需的 RAM单引擎进程批大小顺序1,2,4,8,4,2,1先升后降用于观察批处理在增大与缩小时的行为是否对称计时窗口从最后一个请求发出第一个 token 的时刻开始计时。这样排除了所有串行 prefill 阶段只测量每个已提交请求都处于可批处理 decode的区间复现该实验的 harness测量工具是 c/tools/benchmark_continuous_batch.py它的行为与文档描述完全一致默认批次序列正是1,2,4,8,4,2,1--batches参数默认值即此默认--tokens 32、--kv-slots 8通过环境变量驱动引擎SNAPmodel、SERVE1、SERVE_BATCH1、KV_SLOTS8、COLI_KV_SHARE0、DRAFT0、COLI_TEMP0引擎以[engine, 8, 4, 4]三个位置参数启动对应 CPU 线程/资源配置每个请求以SUBMIT id slot bytes max_tokens 0 1帧发送帧格式见 serve_protocol关键计时逻辑start被设置为最后一个请求base batch产生第一个 token 的时刻measured_tokens统计所有请求在此时间戳之后产出的 token 总数wall end - start。这与文档中排除串行 prefill、只测全批可解码区间的描述一一对应输出聚合aggregate_tok/s、per_session_tok/s、median_tbt_s、p95_tbt_s并对重复批次取中位数计时设计的意义之所以要等最后一个请求发出首 token 才开始计时是因为 mux 的 prefill 是串行的。如果从第一个 SUBMIT 就开始计时测量结果会把 prefill 的串行延迟混进 decode 吞吐里无法区分批处理收益和prefill 排队开销。这个设计保证了 1 槽与 8 槽的对比是纯粹的 decode 阶段对比。结果吞吐饱和在单流上限而非其倍数实验主结果如下来自 实验文档活跃会话数聚合 tok/s各次运行聚合中位数 tok/s每会话中位数 tok/s14.29, 5.394.844.8427.19, 5.476.333.1648.22, 8.078.142.0488.308.301.04实验没有展示聚合吞吐突破。结论要点相对 mux 单槽路径吞吐确有上升4.84 → 8.30 tok/s但 4 会话时已饱和在约 8–9 tok/s这约等于既有的非 mux 单流基线同一冻结放置下 8.90 tok/s而不是它的倍数8 会话的中位 TBTtime-between-tokens为 0.852 sp95 TBT 为 1.306 s——批处理以牺牲单会话延迟为代价却没能把完成的总工作量提升到足以证明这笔交易划算换句话说批量前向没有创造出更多有效吞吐只是把同一份工作切给了多个请求。Profile瓶颈不在 attention而在 CPU 专家带宽用更短的1,4,8序列并开启PROF1性能剖析见 ENVIRONMENT.md 中PROF条目输出前向延迟百分位、专家 IO 总计与缓存层级填充、各阶段墙钟占比复现了同样形状活跃会话数聚合 tok/s前向 p50CPU 专家带宽13.74267 ms26.57 GB/s47.35522 ms约 31–35 GB/s89.05813 ms约 33–35 GB/s关键发现专家 matmul 占总耗时的 69–72%——这是绝对的主瓶颈8 会话时 CPU 专家侧比重叠的 GPU 关键路径慢5.6–11.1 倍对照普通 8.90 tok/s 单流运行在 CPU 专家上达到66.88 GB/sattention 仅占 15–17%不是主要瓶颈为什么多行 CPU 专家路径丢了近一半带宽源码给出了直接证据。在 c/colibri.c 的 MoE 专家处理循环中约 L5927–L6000引擎对每个专家先收集使用它的行int nr0; /* righe (posizioni) che usano questo expert */ for(int s0;sS;s) for(int kk0;kkkeff[s];kk) if(idxs[(int64_t)s*Kkk]eid){ rows[nr]s; rw[nr]ws[(int64_t)s*Kkk]; nr; break; } if(!nr) continue;随后对同一专家分组后的nr行调用expert_ffn(hh,gg,uu,xg,e-g,e-u,e-d,nr,I)并把专家权重字节数计入cpu_expert_bytes用于带宽统计m-cpu_expert_bytesqt_bytes(e-g)qt_bytes(e-u)qt_bytes(e-d); m-cpu_expert_rows(uint64_t)nr;也就是说当前的多行路径确实会把选择同一专家的行分组但分组没有转化为足够的权重复用或并行内存带宽——多行量化 matmul 仍以接近每专家一遍权重的代价运行。实验测得的 26–35 GB/s 对比单行的 66.88 GB/s说明有效 RAM 带宽打了对折。从源码结构看问题出在量化权重流只被单行消耗、未在向量寄存器/缓存中跨行复用。实验中发现的内存陷阱KV 容量与专家驻留是一个决策实验过程中发现了一个重要的部署教训。当使用8 个默认CTX4096槽位配合自动 live-history 放置时只有 9,335 VRAM 5,321 RAM 专家被固定对比全驻留时的 9,335 10,121。第一次运行因此测出命中率 96.6–99.8%聚合中位数仅 3.64 / 3.51 / 5.48 / 6.00 tok/s1/2/4/8 会话文档明确指出这些数字混入了磁盘/LRU 预热效应作为计算对比是无效的。这正是实验文档选择CTX256的原因——8 槽 × 4096 上下文占用的 KV 内存挤掉了约一半 RAM 专家的驻留空间。部署层面的结论服务配置必须在宣称全专家驻留之前预留 KV 内存。KV_SLOTS、CTX与专家放置是一个容量决策而不是三个相互独立的调参旋钮。这个结论在引擎源码中得到印证。c/colibri.c 的run_serve_muxL8832 起同时解析这两个变量int maxctxgetenv(CTX)?atoi(getenv(CTX)):4096; int nctxgetenv(KV_SLOTS)?atoi(getenv(KV_SLOTS)):1; if(nctx1||nctx512){fprintf(stderr,KV_SLOTS must be between 1 and 512\n);exit(2);}每个槽位通过serve_ctx_init(m,ctx[i],snap,i,maxctx)初始化独立 KV 上下文内存占用随nctx × maxctx线性增长而专家驻留受RAM_GB默认约 88% 可用 RAM见 ENVIRONMENT.md和PIN从.coli_usage/stats 文件固定热专家如PINauto共同约束。两者争抢同一块 RAM 预算这正是8 槽默认 CTX4096 挤掉 RAM 专家陷阱的机制根源。部署时应先根据KV_SLOTS × CTX划出 KV 预算再决定剩余 RAM 能驻留多少专家。从源码看批处理路径的实现结构理解结果需要看清 mux 批处理在引擎里是怎么落地的。run_serve_mux的主循环c/colibri.c L8867–L8992大致如下select()POSIX或PeekNamedPipeWindows非阻塞轮询 stdin读取SUBMIT/STOP/CANCEL帧收集所有活跃槽位为DecodeRow数组每个槽位一行调用step_decode_batch(m, rows, S)执行一次批量前向对每个槽位的输出行做pick_tok采样、更新历史、通过mux_data发射DATA id n帧step_decode_batchc/colibri.c L7165–L7200是批处理的核心static float *step_decode_batch(Model *m, const DecodeRow *rows, int S){ Cfg *cm-c; int Dc-hidden; /* Ragged KV currently uses MLA absorption; the stack kernel is sized to 512. */ if(!rows || S1 || S512 || c-kv_lora512) return NULL; KVState *kvs[512]; int positions[512]; ... for(int s0;sS;s){ embed_row(m,rows[s].token,x(int64_t)s*D); } layers_forward_rows(m,x,S,0,kvs,positions); ... matmul_qt(logit,norm,m-lm_head,S); return logit; }它把 S 个槽位的 token 并行 embed 成S×D的激活矩阵调用layers_forward_rows走完所有层最后对S×D归一化结果做matmul_qt一次性算出 S 行 logits。每个槽位共享同一个权重流dense 权重 路由专家权重理论上这正是权重复用的机会所在——但如 Profile 章节所证CPU 专家路径目前未能兑现这一复用。批处理的保护性约束step_decode_batch有两道显式防护拒绝S512栈内核尺寸上限以及拒绝两个行共享同一个KVStatefor(int p0;ps;p) if(rows[p].kvrows[s].kv){ free(x); return NULL; }——每个槽位必须是独立的 KV 上下文这从代码层面保证了批处理确实是在处理多个独立会话。另一个重要约束在多槽模式下自动禁用推测解码。run_serve_mux中/* MTP/n-gram speculation is not ragged-safe across KV slots, so multi-slot * serve keeps one scheduler owning every forward (g_draft0). At KV_SLOTS1 * there IS no ragged batch ... */ if(nctx1) g_draft0; else if(g_draft0) fprintf(stderr,[MTP] single-slot serve: speculation active (draft%d)\n,g_draft);MTP/n-gram 推测在多个 KV 槽位间不是 ragged-safe 的因此KV_SLOTS1时强制g_draft0这正是实验中DRAFT0的代码级原因KV_SLOTS1时则保持正常推测。这解释了为什么 1 槽路径与多槽路径在解码策略上本就不完全对等——也是单流基线能到 8.90 tok/s 而 mux 1 槽只有 4.84 tok/s 的一个结构性因素单流走spec_decode/非 mux 路径。结论与后续有界实验官方结论连续批处理在功能上已经实现但当前的 CPUGPU 执行路径无法把 GLM-5.2 的聚合 decode 吞吐提升到现有单流上限之上。在这一点上它还不能被宣传为吞吐倍增器。文档给出的下一个有界实验方向非常具体专用的多行 INT4 CPU 专家内核——把每个专家的权重解码一次到向量寄存器/缓存中在推进权重流之前累加所有行。整合它的准入条件是至少恢复单行路径 60 GB/s 的有效带宽把全驻留 4/8 会话聚合吞吐抬升到 8.9 tok/s 之上对部署者的实操建议结合实验结果与 ENVIRONMENT.md 的配置参考做吞吐对比前先用PIN冻结专家放置并确认全驻留stderr 中 TIERS 行会打印各层驻留专家数与字节数否则命中率抖动会让测量失真把KV_SLOTS × CTX当作一个容量决策KV 内存与专家驻留共享 RAM 预算先划 KV 再定驻留多槽服务会强制关闭推测解码g_draft0对比单槽基线时注意这不是同一解码策略若 CPU 是瓶颈主机可关注COLI_GROUP_ASYNCCUDA 专家分组异步 issue/collect 以实现 CPU/GPU 解码重叠ENVIRONMENT.md L82、XEXP单个 OpenMP 区域跨批联合块内全部专家S1 且全驻留 int4 块时启用测量 11.6% 于 48 核双路、对 24 核中性偏负需在本机实测等专家路径调优开关而 attentionCOLI_CUDA_ATTN、COLI_CUDA_PIPE在本实验的 profile 中不是主要瓶颈实验制品Artifacts实验数据与 stderr 日志保存在实验环境的如下路径外部路径仅供交叉核对全驻留对比运行/data/test/colibri-contbatch-20260731-IucO7Z/continuous-batch-fullresident-abba.json与.stderr剖析运行/data/test/colibri-contbatch-20260731-IucO7Z/continuous-batch-profile.json与.stderr无效的内存压力对照组8 槽默认 CTX4096 自动放置混入磁盘/LRU 预热/data/test/colibri-contbatch-20260731-IucO7Z/continuous-batch-abba.json对照三组制品即可完整复现本文的推理链全驻留对比 → 剖析定位 → 内存陷阱对照组为何无效。相关工程依据可继续阅读 serve_protocol.mdmux 协议帧格式、ENVIRONMENT.md全部环境变量语义、SETTINGS.mdcoli serve与openai_server.py的 CLI 参数如--kv-slots映射到KV_SLOTS以及 c/tools/benchmark_continuous_batch.py可复跑的 harness 与计时逻辑。【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表