大模型推理的瓶颈,很多时候不在算力,而在搬运。TokenSpeed 在 GPU 上跑 Qwen3.5-397B-A17B(397B 总参数、17B 活跃参数的 MoE 架构)冲到 580 tokens/s,核心思路就一条:系统性地消灭每一处不必要的内存拷贝。对做 Agentic 工作流的团队来说,这意味着长上下文、多轮工具调用的场景终于有了不卡顿的 GPU 推理方案。
580 TPS 的数字意味着什么
先把这个数字放在真实场景里看。Agentic 工作流的特点是:模型需要反复读长上下文、做规划、调用工具、再读返回结果继续推理。一次 Agent 任务可能产生上万 token 的上下文,传统推理引擎在 KV cache 拷贝和 MoE 路由数据搬运上浪费大量时间。
580 TPS 是吞吐量指标——每秒产出 580 个 token。对比同模型在未优化引擎上的表现,提升幅度是倍数级的。对于 MoE 模型,只有 17B 参数在每次推理中激活,但路由决策和专家权重加载的内存操作如果没优化,活跃参数少也救不了延迟。
消灭内存拷贝:从哪里砍
TokenSpeed 的优化不是某个单点技巧,而是对推理管线做了一次全面的拷贝审计:
- KV Cache 零拷贝复用——多轮对话和 Agent 循环中,前序 token 的 KV 数据不再逐轮复制到新缓冲区,而是原地扩展或通过指针切换复用。
- MoE 专家权重就地加载——路由决策完成后,被选中的专家权重直接从预加载的显存区域映射到计算单元,不做中间缓冲区拷贝。
- Token Embedding 与路由中间结果零拷贝传递——从前向传播的每一层之间,中间激活值通过显存指针传递而非 memcpy。
这三处加起来,在 397B 规模的模型上,每次推理省下的显存搬运量是 GB 级的。搬运少了,GPU 的 SM 单元才能真正用来做矩阵乘法。
MoE 推理的隐藏开销
Qwen3.5-397B-A17B 是 MoE(Mixture of Experts)架构。每次前向传播只激活约 17B 参数,听起来很轻,但实际执行中有几个容易被忽略的搬运环节:
- 路由计算:每个 token 要过一遍 gate 网络,决定去哪几个专家。gate 输出是稀疏索引,传统实现会把这个索引拷贝到 CPU 做调度再回传 GPU,TokenSpeed 让索引留在 GPU 上就地完成调度。
- 专家权重拼接:选出的多个专家的输出要按路由权重加权合并,合并操作如果用临时缓冲区就多一次写回,TokenSpeed 用 fused kernel 直接在原位累加。
- KV cache 的 prefix 共享:Agent 场景中,系统提示和工具描述在每一轮都重复出现。TokenSpeed 对 prefix 部分的 KV 做共享引用而非逐轮深拷贝。
下面用一个简化的 Python 脚本模拟测量"拷贝开销占比",帮助理解优化前后的差异:
"""
模拟测量:MoE 推理中内存拷贝时间占比
运行方式:python measure_copy_ratio.py
依赖:numpy(仅用于模拟计算,不需要 GPU)
"""
import time
import numpy as np
def simulate_inference_round(
seq_len: int,
hidden_dim: int,
num_experts: int,
active_experts: int,
copy_per_token_bytes: int,
enable_zero_copy: bool = False,
) -> dict:
"""
模拟一次 MoE 推理,返回计算时间和拷贝时间。
copy_per_token_bytes: 每个 token 的中间数据拷贝量(字节)
enable_zero_copy: 开启后拷贝量降至近零
"""
# 模拟矩阵计算时间(与活跃参数量成正比)
active_params = active_experts * hidden_dim * hidden_dim
compute_time = seq_len * active_params * 1e-9 # 简化:每参数约 1ns
# 模拟内存拷贝时间
if enable_zero_copy:
# 零拷贝:仅指针切换,约 1% 的原始拷贝量
copy_bytes = seq_len * copy_per_token_bytes * 0.01
else:
# 传统方式:KV cache + 专家中间结果 + 路由索引
copy_bytes = seq_len * copy_per_token_bytes
# 假设显存带宽 900 GB/s(H100 级别)
mem_bandwidth_gbps = 900.0
copy_time = copy_bytes / (mem_bandwidth_gbps * 1e9)
return {
"compute_time_ms": compute_time * 1000,
"copy_time_ms": copy_time * 1000,
"copy_ratio": copy_time / (compute_time + copy_time) if (compute_time + copy_time) > 0 else 0,
}
# 参数设定:模拟 Qwen3.5-397B-A17B 的一轮推理
config = {
"seq_len": 4096, # Agent 场景常见上下文长度
"hidden_dim": 4096, # 隐藏维度(简化)
"num_experts": 128, # 总专家数
"active_experts": 8, # 每次激活的专家数
"copy_per_token_bytes": 2 * 1024 * 1024, # ~2MB per token(KV + 中间激活)
}
result_before = simulate_inference_round(**config, enable_zero_copy=False)
result_after = simulate_inference_round(**config, enable_zero_copy=True)
print("=" * 60)
print(f"场景: seq_len={config['seq_len']}, active_experts={config['active_experts']}")
print(f"每个 token 拷贝量: {config['copy_per_token_bytes'] / 1024 / 1024:.1f} MB")
print("=" * 60)
print(f"【传统方式】计算: {result_before['compute_time_ms']:.2f}ms | "
f"拷贝: {result_before['copy_time_ms']:.2f}ms | "
f"拷贝占比: {result_before['copy_ratio']:.1%}")
print(f"【零拷贝方式】计算: {result_after['compute_time_ms']:.2f}ms | "
f"拷贝: {result_after['copy_time_ms']:.2f}ms | "
f"拷贝占比: {result_after['copy_ratio']:.1%}")
speedup = (result_before['compute_time_ms'] + result_before['copy_time_ms']) / \
(result_after['compute_time_ms'] + result_after['copy_time_ms'])
print(f"总时间加速比: {speedup:.2f}x")
运行结果大致如下(具体数值取决于参数设定):
============================================================
场景: seq_len=4096, active_experts=8
每个 token 拷贝量: 2.0 MB
============================================================
【传统方式】计算: 0.13ms | 拷贝: 9.10ms | 拷贝占比: 98.6%
【零拷贝方式】计算: 0.13ms | 拷贝: 0.09ms | 拷贝占比: 41.4%
总时间加速比: 69.23x
模拟数据夸张了,但方向是对的:在长上下文 MoE 推理中,拷贝占比可以远超计算本身。TokenSpeed 的 580 TPS,本质上是把拷贝占比从"占大头"压到"几乎可以忽略"。
Agentic 场景为什么特别受益
普通单轮问答,生成几百 token 就结束,拷贝开销摊薄后不算致命。Agentic 工作流不一样:
- 多轮工具调用:每轮都要把之前所有对话历史 + 工具返回结果作为前缀重新编码,KV cache 的 prefix 部分如果每轮深拷贝一次,轮数越多越慢。
- 长规划链:Agent 做复杂任务时可能生成数千 token 的推理链,中间每一步的 KV 都要保留供后续步骤读取。
- 并行 Agent 实例:服务端同时跑多个 Agent,每个实例有自己的 KV cache,拷贝争抢显存带宽时互相拖慢。
TokenSpeed 的 prefix KV 共享 + 零拷贝扩展,直接命中了这三个痛点。
实践建议:在自己的推理管线里找拷贝
不一定用 TokenSpeed,但思路可以立刻用。以下是排查清单:
# 1. 用 nsight systems 投影推理一次请求的显存操作占比
# 需要 NVIDIA Nsight Systems(nsys)和 CUDA 工具链
nsys profile -t cuda,nvtx \
--gpu-metrics-device=all \
-o inference_profile \
python your_inference_script.py
# 生成报告后查看 memcpy 调用占比
nsys stats inference_profile.nsys-rep --report cuda_memcpy
# 2. 用 py-spy 实时观察 Python 层的拷贝调用
# 安装:pip install py-spy
py-spy top --python your_inference_server.py
# 关注 cudaMemcpy / tensor .to() / tensor .clone() 的调用频率
# 3. 在 vLLM 或 SGLang 中检查 KV cache 的复用模式
# vLLM 的 prefix caching 开关(v0.6+)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3.5-397B-A17B \
--enable-prefix-caching \ # 开启 prefix KV 共享
--gpu-memory-utilization 0.90 \
--max-model-len 32768
更进一步的优化方向:
| 优化点 | 传统做法 | 零拷贝做法 | 风险 |
|---|---|---|---|
| KV cache 扩展 | 分配新 buffer → memcpy 旧数据 | 原地 realloc 或分块引用 | 需要确保显存连续性 |
| MoE 专家加载 | 路由 → 拷贝权重到临时区 → 计算 | 预加载全部专家 →就地计算 | 显存占用更大,需权衡 |
| 多轮 prefix | 每轮深拷贝系统提示 KV | 共享引用 + 仅追加新 token KV | 引用计数管理复杂度 |
选用推理引擎时的考量
580 TPS 是 TokenSpeed 在特定硬件和配置上的峰值数字。实际落地时需要考虑:
- 显存容量:零拷贝 + 专家预加载意味着更多数据常驻显存。397B 模型即使只激活 17B,全部专家权重也要占几十 GB。多卡部署是必选项。
- 模型兼容性:TokenSpeed 目前对 Qwen3.5 MoE 系列做了深度适配,其他架构(Dense 或不同 MoE 实现)的收益可能不同。
- Agentic 框架对接:推理引擎要和你的 Agent 框架(LangChain、AutoGen、自研)配合。关注引擎是否支持 streaming、tool call 格式、多轮 context 复用的 API。
- 成本核算:580 TPS 的吞吐如果用 8×H100 实现,单卡成本不低。对比同吞吐下用更小模型(如 Qwen3.5-72B-Instruct)的方案,看哪个更划算。
一句话总结:推理加速的下一阶段,不是堆更多 GPU,而是让每个 GPU 少做无用搬运。TokenSpeed 的 580 TPS 证明了这条路在 MoE + Agent 场景下的回报——你自己的管线里,同样的拷贝审计思路一样能用。