高级功能
让 TensorSharp 既快又可扩展的系统:带分页 KV 缓存的连续批处理、推测解码、文本扩散,以及底层的内核级与内存级优化。
连续批处理与分页 KV 缓存
服务器的 InferenceEngine 是一个 vLLM 式连续批处理引擎,默认开启。它不再一次只运行一个请求,而是以单个 decode 步的粒度将多个请求交错运行。
- 分页 KV 池 —— KV 缓存被划分为取自共享池的定长块,因此内存按块分配,而非按最坏情况的序列长度分配。
- Radix 前缀缓存 —— 在服务器、CLI 生成与 TensorAgent 中默认开启:一棵基于 token 前缀的 radix 树索引为每个新请求找到已缓存的最长前缀(系统提示、工具声明、之前的轮次),在并发与顺序请求间复用而非重新计算,同时遵守会话作用域、媒体身份与各模型可恢复的边界。它覆盖 Qwen 3.5/3.6/3.8、Gemma 4、GLM 5.x、Qwen 3.8 Flash Next、DeepSeek V4/V4.1 与原生执行器上的 Muse Spark(模型自有的 holder 与结束状态),以及 GPT OSS、Mistral 3、Hunyuan Dense、Muse-Glimmer 与 Nemotron-H(共享的分页块);DiffusionGemma 以及图像、视频模型不使用它。
--no-prefix-cache(TS_SCHED_PREFIX_CACHE=0)会关闭复用。 - 迭代级调度器 —— 在批中接纳与抢占序列,并在实现
IBatchedPagedModel的模型上把它们打包进一次前向。
尚未实现批量路径的模型仍运行在引擎隔离的逐序列 KV 交换回退之上。用 TS_SCHED_* 变量调参,或用 --no-continuous-batching 完全禁用。
原生分页注意力
原生内核 TSGgml_PagedAttentionForward(以及面向 GPT OSS 的 WithSinks 变体)在 C++ 中从分页缓冲收集 K/V,为每个序列构建一个小的 GGML 图,并分派 ggml_flash_attn_ext —— 与单序列路径所用相同的融合 Metal/CUDA 闪存注意力内核。在长上下文 Ministral-3-14B 负载(4×约 800 token)上,它比逐序列 GGML 路径快约 21%。
MLA / 稀疏注意力架构(DeepSeek V4、GLM 5.x)走的是第三条路。每个 token 只有一行压缩缓存,没有可分页的布局,因此原生执行器给每个请求分配一个槽位——一整套逐层的 MLA 与索引器缓存,外加它自己的 n_past——绑定请求只是切换活跃槽位,不搬运任何 KV 字节。GLM 的批处理融合解码默认启用:一张图让 N 个序列各出一个 token,权重在它们之间只读一次——4 路并发下总解码吞吐 1.81 倍。设置 TS_BATCHED_FUSED_DECODE=0 可切回串行融合 decode,以便严格比较路径。批处理会改变每一个 GEMM 的形状,而 78 层里有 75 层是 top-8-of-256 路由,可能把最低位的差异放大成 2-bit 权重上不同的专家选择。
批量前向(Mistral 3、Gemma 4、GPT OSS、Hunyuan Dense、带 GatedDeltaNet 循环状态池的 Qwen 3.5/3.6,以及带 Mamba2 循环状态池的 Nemotron-H)把 N 个序列打包进一次 ForwardBatch 调用,每层一次批量线性投影 matmul,并配以分页 K/V 散写。Gemma 4 达到约 1.5–1.6× 单序列吞吐;Nemotron-H Mamba2 批量在 Apple M4 Pro 上 batch=3 时达到约 3.95×。
共享前缀 checkpoint
对普通注意力模型而言,复用已缓存的 KV 页就够了。但对状态存放在递归状态或融合 holder 里的模型,在每一个会话都要以之开头的那段提示词——系统提示词、工具声明、选中的技能——的末尾,仅靠分页并不够,每个新会话原本都要把它从头 prefill 一遍。在 GGML 后端上的 Gemma 4 与 Qwen 3.5/3.6/3.8 家族,以及 Qwen 3.8 Flash Next(包括其按层切分)上,引擎会在这段共享前缀的末尾对完整模型状态做 checkpoint,并让每个新会话从它的副本开始,于是新会话只需重新 prefill 自己的那条消息;两个 Qwen 家族在张量并行下都不做 checkpoint。Muse Spark 的原生执行器用一个 checkpoint 槽位做同样的事:共享前缀边界之前的 token 只复制进该槽位一次,再克隆进每个新会话;由于它的模板让每个会话都以相同的系统头部开场,即使没有系统消息,这段头部也是共享前缀。宿主声明的共享前缀至少为 64 个 token。TS_PREFIX_CHECKPOINTS_MAX(默认 4)限制同时保留 checkpoint 的不同前缀数量;设为 0 可关闭。
宿主可以给引擎挂上 IPrefixCheckpointStore,让 checkpoint 活得比进程更久——执行器会在准入阶段读取已保存的 checkpoint,并在取 checkpoint 时写回,字节格式由模型家族自己拥有并校验(Gemma 4 与 Qwen 3.5 家族支持导出)。TensorAgent 正是这样让每次启动的第一条消息只付出恢复的代价而不是完整 prefill:在 iPhone 17 Pro Max 上以 Qwen3.5 9B 实测,原本 54 秒的冷启动首条消息,变成 1.2 秒预热加约 0.6 秒的首条消息。服务器默认也这样做:它在打开端口之前为启动模型前向一遍共享提示词,并把 checkpoint 存在二进制旁的 prefix-cache/ 中,每个模型一个目录(TENSORSHARP_PREFIX_CACHE_DIR 可改位置)——在一份智能体配置上实测首条消息从 21.8 s 降到 0.7 s。--no-prefix-cache 会同时关闭复用、启动预热与持久化。CLI 的缓存只在进程生命周期内保存在内存里。PrefixCheckpointExactnessTests 证明:从恢复的副本开始的会话,与冷 prefill 产出相同的 token。
与之配套的还有终态保留(默认开启;TS_RETAINED_FUSED_CACHE_MAX 限制保留的数量,默认取运行中请求上限,使每个并行运行的请求都保有自己的状态,设为 0 关闭),它保留已结束请求的融合 holder,使前缀完全一致的续写不必重新 prefill —— 适用于 Gemma 4、Qwen 3.5 家族与 Qwen 3.8 Flash Next。DeepSeek V4.1 与 GLM 5.x 会为每个已结束会话的下一轮保留它的原生 slot(V4.1 预算 TS_DSV41_RETAINED_CACHE_MB,默认 2048;加载了 DSpark 草稿器时不保留),Muse Spark 的原生执行器也是如此,并且还能回退该 slot 以重新生成回答。此外还有三个 K/V 尺寸旋钮——TS_KV_INITIAL_TOKENS、TS_KV_GENERATION_RESERVE_MAX、TS_KV_HOLDER_POOL_MAX——让内存受限的宿主把缓存从小起步、按需增长,而不是每个请求都预留整个上下文窗口。
完整深入解析:仓库中的 docs/PAGED_ATTENTION_AND_CONTINUOUS_BATCHING.md。
MTP / NextN 推测解码
部分架构自带一个多 token 预测(MTP / NextN)草稿头,让服务器可为单序列(非并发)运行无损推测解码。草稿廉价地提出若干未来 token,主干在一次批量前向中验证全部,被接受的 token 一步提交。
由于请求自身的采样器 —— 温度、top-k/p 与全部惩罚 —— 同时驱动草稿与验证,输出与标准解码完全一致。推测只改变产生相同 token 所需的前向次数。
它默认关闭。内嵌在主干 GGUF 里的草稿头在 CLI 或服务器上用 --spec(环境变量 TS_SPEC=1)启用;以独立 GGUF 发布的草稿头则只需用 --draft-model 写出文件即自动启用(显式的 --no-spec 仍可否决):
# Qwen 3.6 —— 必须使用保留 NextN 块的 -MTP- 仓库 GGUF;基础仓库导出会剥离该块
dotnet run --project TensorSharp.Server.Host -c Release --no-build -- --model Qwen3.6-35B-A3B-UD-IQ2_XXS.gguf --backend ggml_cuda \
--spec --spec-pmin 0.75
# Gemma 4 —— 加载与目标匹配的独立 gemma4-assistant 草稿 GGUF
dotnet run --project TensorSharp.Server.Host -c Release --no-build -- --model gemma-4-12B-it-qat-UD-Q4_K_XL.gguf --backend ggml_cuda \
--draft-model mtp-gemma-4-12B-it.gguf
草稿头形态
- Qwen 3.6(内嵌 NextN) —— GGUF 携带一个额外解码块以及 NextN 投影/归一化张量。无需独立文件;
--draft-model在此只接受 DFlash/DFlash2 草稿器,挂上后取代 NextN 头(--tp N> 1 下会被拒绝挂载:CLI 打印警告并按标准解码服务,服务端拒绝启动);其他草稿 GGUF 不会被挂载 —— 服务器会拒绝启动,CLI 则给出警告。只有 unsloth/Qwen3.6-35B-A3B-MTP-GGUF 一类保留 NextN 张量的导出才具备该块;基础仓库导出会剥离它并静默回退到标准 decode。GatedDeltaNet 主干状态会被快照,以便部分被拒绝的验证批可回滚。 - Gemma 4(独立
gemma4-assistantGGUF) —— 一个 EAGLE 式循环草稿器,用--draft-model加载(写出该文件本身即启用投机)。它自身不持有 K/V:每个草稿层都查询目标模型已有的逐层 KV 缓存。草稿的隐藏维度必须与目标一致(把 12B 目标与其 12B 草稿配对)。不匹配或不完整的草稿会在启动时立即失败并给出修复提示。 - Qwen 3.8 Flash Next(独立的共享 MTP 头) ——
--draft-model mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf在 GGML 后端上给qwen4exp挂上一个逐 token 的 MTP 块。它持有自己的 K/V,因此只为从位置 0 开始 prefill 的单序列请求起草;延续已保留 holder 或共享前缀副本的轮次按普通方式 decode。模型卡记录(三卡 A40 按层切分、--spec-draft 3):使用当前逐位一致的验证内核时,代码复写流为 1.69×(2026-09-17 验证行改动之前为 1.75–1.96×);散文则是净亏损(单请求 44–46 对 52 tok/s)。
推测解码与 radix 前缀缓存。--spec 不再关闭前缀复用。Qwen 3.5/3.6/3.8 的 NextN 头在复用前缀(radix 命中、启动预热或从磁盘恢复的共享前缀、保留的对话轮次)结束处只重启它私有的注意力缓存,主干保留完整的缓存状态,因此 prefill 仍走正常的快路径;在草稿头重建历史之前,初始接受率可能偏低。这适用于单序列请求的默认融合验证路径;关闭融合验证而选中的分页推测路径仍需从位置 0 prefill。Gemma 4 的 assistant 头不持有状态,可从任意位置起草;块级草稿器(DFlash、DSpark)与 n-gram 推测器同样可以在复用前缀之后启用;Qwen 3.8 Flash Next 的头则不行。
GLM-5.3(非 Flash 版,GGUF 架构 glm-dsa)同样自带内嵌形态:在主干 GGUF 的 blk.78 上完整携带一个 NextN 块,位置与 GLM-5.2 自己那一个相同。--spec 必须在加载之前就出现在命令行上,因为加载器是在读取主干的同时决定要不要把该块一并读进来(TS_SPEC)。该块不含 nextn.shared_head_head.weight,只能借用主干的 LM head,因此投机在单设备或显式按层切分模式(--layer-split N,不启用张量并行)时生效:--tp N>1 时这个 head 是按列切分的,加载器拒绝用某个 rank 手上的那一条词表切片来起草。这条约束是从原生与托管两侧的加载器代码读出来的,并没有测试覆盖;它也不是 GLM-5.3-Flash 的限制 —— glm5next 的 NextN/MTP 根本没有实现,与「某种模式下不可用」是两回事。下表只给出 Qwen 3.6 与 Gemma 4;GLM-5.3 的内嵌 NextN 目前没有属于它自己的接受率或加速数字。完整机制见仓库中的 docs/models/glm.md 卡片。
何处有收益
| 后端 | Qwen 3.6 | Gemma 4 |
|---|---|---|
| GGML CUDA / GGML Metal | ✅ 融合验证 + 草稿内核 | ✅ 融合验证 + 草稿内核 |
直接 CUDA(cuda,纯 C#) | ✅ GPU 常驻逐 op 验证/草稿 | ✅ GPU 常驻逐 op 验证/草稿 |
| GGML CPU / GGML Vulkan | ✅ 会推测 —— 在 Qwen3.6-35B-A3B ggml_cpu 上以 8 token 窗口实测 1.21×(接受率 86%),该测量早于该家族默认窗口收窄为 3 token;当前默认值下没有已提交的实测数字 | ✅ 会推测 —— Gemma 4 的门限是任意 GGML 后端 |
| CPU / MLX | 不按后端关闭 —— 只要草稿头能加载就遵从 --spec;未实测加速 | 标准解码 |
调参:--spec-type auto|draft-head|block|ngram 选择算法 —— auto(默认)使用检查点自带的草稿器,ngram 是在序列自身 token 上做后缀匹配、无需权重的推测器,不需要草稿文件。但 n-gram 仍需要模型是推测目标,因此并非处处可用:它可运行在 Qwen 3.5/3.6/3.8 家族、Gemma 4(GGML 后端或 cuda)、GLM 5.x(GLM-5.3-Flash 仅在其回滚可用时)、Qwen 3.8 Flash Next(GGML)与 Muse Spark(它唯一的推测方式;其原生 --tp N 下不推测,只做普通 decode)上,而不能运行在 GPT OSS、Mistral 3 或 Hunyuan Dense 上;DeepSeek V4/V4.1 与 Muse-Glimmer 只有挂上各自的块级草稿器才会推测,Nemotron-H 则拒绝一切推测器。--spec-draft(默认 8)限定每步草拟的 token 数;--spec-pmin 是保留某 token 所需的最低草稿置信度,其默认值取决于草稿器类型(逐 token 草稿头为 0.15,块级草稿器为 0.35 —— 后者的门限是累积前缀概率,因此同一个数字要严格得多)。在 GGML 后端上对 Qwen 3.5 显式传入的 --spec-draft 请保持 7 或更小:验证批达到九行时会偏离普通贪心解码,这是正确性缺陷而不是速度问题。默认值不受影响,因为该模型自己要求 3 token 的窗口。
DSpark 块级投机解码(DeepSeek V4)
DeepSeek V4 的 checkpoint 中随模型附带一个 DSpark 支持模块("Confidence-Scheduled Speculative Decoding with Semi-Autoregressive Generation"):三个 DSV4 块读取主干的 hidden states,每步提议一整块 token 而不是一个;一个 Markov 头让块内每个位置以其前一个 token 为条件;一个置信度头预测每个位置被接受的概率。主干随后用一次批量前向验证整块,只保留它自己的采样器本来也会产生的前缀。
草稿器是一个独立的 GGUF,用 --draft-model 加载 —— 主干的所有 GGUF 转换都会丢掉 mtp.* 张量。预构建的草稿器列在仓库的 MODEL_DOWNLOADS_zh-cn.md 中,也可以用 eng/dsv4-dspark-to-gguf.py 从上游 safetensors checkpoint 自行转换(只需下载含 mtp.* 的三个分片)。
# CLI —— 所有单序列路径(--input、--multi-turn-jsonl、--interactive)都会用到
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model DeepSeek-V4-Flash-...-00001-of-00005.gguf \
--backend ggml_cuda --layer-split 4 --draft-model DSpark-drafter-Q2K-Q8-0731.gguf \
--input prompt.txt --max-tokens 200 --temperature 0
# 服务端 —— 同一个草稿器参数;写出文件即启用投机
dotnet TensorSharp.Server.Host/bin/TensorSharp.Server.Host.dll --model DeepSeek-V4-Flash-...-00001-of-00005.gguf \
--backend ggml_cuda --layer-split 4 --draft-model DSpark-drafter-Q2K-Q8-0731.gguf
它可运行在两个 GPU 引擎上 —— --backend cuda(Direct CUDA)与 --backend ggml_cuda(原生 ggml);ggml_vulkan、ggml_metal、ggml_cpu 与 cpu 对该架构没有投机路径,配置了草稿器时会打印警告。在 ggml 上草稿器就是计算图里额外的三层,其 key ring 由主干图自己提交,因此投机不产生任何主机往返。CLI 与服务端都通过共享引擎运行它,每一行验证都用该请求自己的采样器,因此可与任意采样设置组合。投机只服务单序列 —— 一旦有第二个请求在途,DSV4 的 per-sequence slot 会以正常 decode 速度服务这一批。
实测(4×A40,DeepSeek-V4-Flash-0731 UD-Q8_K_XL,贪心)
| 指标 | cuda 基线 | cuda + DSpark | ggml_cuda 基线 | ggml_cuda + DSpark |
|---|---|---|---|---|
| Decode(生成 200 token) | 26.0 tok/s | 34.0 tok/s(1.31×) | 26.4 tok/s | 37.1 tok/s(1.41×) |
| Prefill(15K 提示词) | 962 tok/s | 955 tok/s | 952 tok/s | 954 tok/s |
| 接受率 | — | 69% | — | 69% |
多轮对话获益最大 —— 延续既有上下文的轮次,正是草稿器最有把握的地方。在同一台机器上的 5 轮交互式会话中,每轮实测 1.50× 到 2.02×(接受率 66–87%),prefill 保持持平。在 200 token 与 15K 上下文两次运行中,贪心输出都与非投机基线逐字节一致。
为什么是 1.3× 而不是更多:主干是 6 选 256 的稀疏结构,验证批中每多一个 token 就要把它自己那套路由专家拉过显存 —— 无论起草多便宜,一行验证都要花掉约四分之一个完整 decode 步。--spec-pmin(块级草稿器默认 0.35,即保留某个起草位置所需的最小累积接受概率)正是让这笔交易保持正收益的旋钮;--spec-draft 则限制每块的 token 数。
以上实测数字都不适用于 DeepSeek V4.1,它的 DSpark 支持是实验性的。加载器只在 ggml_cuda 与 ggml_cpu 上接受通过 --draft-model 指定的 deepseek41-dspark 草稿器,并拒绝 V4 草稿器或其他任何草稿架构。在其他所有后端上 —— 包括 --backend cpu 这个 100% 纯 C# 的 V4.1 执行器,以及 Direct CUDA 引擎 —— 配置了草稿器都会被硬拒绝("DeepSeek V4.1 DSpark requires the TensorSharp ggml executor (--backend ggml_cuda or ggml_cpu) and a matching deepseek41-dspark drafter. Other executors do not implement the V4.1 draft graph."),而不是 V4 的「告警后继续」。训练模型已在 ggml_cuda 双 GPU 按层切分下通过初步文本/图像 HTTP 检查;尚不构成通用质量或吞吐验证。
DFlash / DFlash2 块级投机解码(Muse-Glimmer、Qwen 3.5 家族)
Muse-Glimmer 有自己的块级草稿模型 DFlash:一个独立的 5 层 GGUF(general.architecture = dflash),一次前向即提出整个投机窗口。它复用主干的 token embedding 与 LM head,维护自己的滑动窗口 KV 环,并按三步驱动——把主干在 dflash.target_layers 处的逐层输入残差编码成一行宽向量,将该行注入为每个草稿层的 K/V,然后把 [anchor, MASK × (block−1)] 送过 5 个块起草,并用主干的 LM head 打分。随后主干用一次批量前向完成验证。
dotnet TensorSharp.Cli/bin/TensorSharp.Cli.dll --model Muse-Glimmer-30B-UD-IQ2_XXS.gguf \
--backend ggml_cuda --draft-model dflash-kquant.gguf --spec-draft 15 \
--input prompt.txt --max-tokens 256
两部分都是可被 CUDA 图捕获并重放的融合原生图:TSGgml_DFlashInject 与 TSGgml_DFlashDraftBlock,后者以设备端 argmax 收尾,因此 202048 宽的概率块无需经 PCIe 回传主机。每一行验证都用该请求自己的采样器抽取,因此贪心运行输出的就是普通贪心流。运行期的成本调控器会把投机与普通解码逐 token 计时对比,在投机确实更慢时暂停起草——这保证投机只会更快,但也意味着它需要生成数百个 token 才能稳定,因此很短的回答可能看不到收益。在单张 RTX PRO 6000 Blackwell 上与 llama.cpp 自带的 DFlash 实现对比(Q8_0、贪心、60 token 提示):50.9 tok/s,对比 llama.cpp 的 45.5 与普通解码的 35.0。完整的上下文阶梯(包括 llama.cpp 领先的区间)见仓库中的 docs/models/muse-glimmer.md 卡片。
DFlash2 是同一套主干加上两处新增,两者都由 GGUF 自身标明,因此同一条代码路径可服务两代草稿器:一是在每个注意力与 FFN 子层周围加入分组动态深度卷积,让块扩散式起草无需第二次前向就获得局部的从左到右信号;二是一个候选选择器,它通过两个低秩 [vocab, r] 码本对相邻位置的 top-K 候选做两两打分,并把整块读作在该格状图上的一次游走,于是位置 i+1 不再是在不知道 i 选了什么的情况下被选出来的。两者都用 --draft-model 挂载——文件本身会说明它是哪一代。两者也都可以以 Qwen 3.5 / 3.8 主干为目标;Qwen 3.8 检查点还可能同时内嵌 NextN 块,挂上 DFlash 草稿器时以后者为准。
Nemotron 3.5 Lightning 的 DSpark 模块不会被挂载。NVIDIA 为它发布了一个 6 层 DSpark 草稿器(SWA-1024、每头的 attention-sink 偏置、在第 2/6/20/30/42/52 层读取主干残差的编码器,以及 rank-512 的 Markov 头),eng/nemotron-dspark-to-gguf.py 也仍可把它导出为 dflash 架构的 GGUF。但 Nemotron-H 拒绝一切推测器,n-gram 也不例外:它的多 token 验证与单 token decode 走的是不同的注意力与 MoE 内核,logits 并不相同,推测流因此无法与普通贪心 decode 一致,而逐位一致的验证代价与逐行 decode 相当。用 --draft-model 指定的草稿器不会被挂载:CLI 会报告并按普通方式 decode,服务器则拒绝启动,直到去掉该参数。
DiffusionGemma 文本扩散
DiffusionGemma 与自回归模型本质不同:它不逐 token 调用 Forward()。相反,它在 Gemma-4 衍生的 MoE 主干上,对定长画布做块式 EntropyBound 去噪 —— 整个答案是被迭代精炼,而非从左到右写出。
- CLI ——
--diffusion-steps(每块去噪步数)、--diffusion-seed与--diffusion-blocks。 - Web UI —— 流式发送整条消息的
replace事件,让你实时观看答案去噪,并通过DiffusionBatchScheduler在块边界批处理并发扩散请求。 - 优化 —— 在 GPU 后端与纯 C# 的
cpu后端上,[prompt | canvas]的提示侧每块预取一次并跨步复用;GGML 后端使用融合的整模型扩散 decode 加上融合的 lm-head 尾部。
性能优化
这是跨架构的概要;docs/models/ 中每个模型卡都会以确切分派的 GGML 图逐一讲解相同内核。
已验证的 Gemma 4 E4B Q8_0 快路径:仓库基准已在 GPU 后端上验证原生 GGML 的 E4B Q8_0 家族与执行路径。推荐从公开的 ggml-org/gemma-4-E4B-it-GGUF 下载。
- 单图 GPU decode(Gemma 4 dense,包括 E4B)—— 所有 transformer 层在一次 GGML 图分派中完成,把每 token 的 CPU↔GPU 往返从数百次降到一次(相对逐 op 分派约 2.6×)。
- 整模型融合 decode 计算图(Gemma 4 dense + MoE、Qwen 3.5/3.6、GPT OSS)—— 一个 decode token 的全部计算(含 MoE 路由与专家、最终 norm 与 LM head)作为一次 GGML 计算图提交。在 CUDA/Vulkan 上该图只构建一次、张量地址保持稳定后反复重放,这正是 ggml-cuda 能把它捕获成 CUDA 图的前提。GPT OSS decode 在 A40 上从 24 → 154 tok/s,且随上下文长度基本持平(16K 时 133 tok/s,而逐层路径已跌到 2.3)。
- DeepSeek V4 整模型执行器 ——
deepseek4完全绕开通用的逐算子前向。每套 GPU 执行器自行加载分片 GGUF,通过--layer-split N将整层放到 N 张本地 GPU(默认单设备),在设备上持有全部 DSV4 KV 状态(原始 SWA 环、CSA/HCA 压缩 K 缓存、lightning indexer 缓存),并把每个 prefill/decode 微批作为一张计算图执行,配合按形状签名的缓存,使稳态 decode 直接重放已捕获的 CUDA 图。decode 注意力通过融合的 index-gather 取出紧凑的[ring | top-512]K,而不是扫描整个上下文。 - GLM 5.x 整模型执行器 ——
glm-dsa的做法相同,而且同一套架构描述符同时服务 GLM-5.2、GLM-5.3 与 GLM-5.3-Flash:原生执行器自行加载分片 GGUF(GLM-5.2 UD-IQ2_XXS 为 6 个分片,GLM-5.3 UD-Q2_K_XL 的 236.4 GiB 为 7 个分片),通过--layer-split N将整层放到 N 张本地 CUDA 或 Vulkan GPU(默认单设备),在设备上持有 MLA 与 lightning indexer 的缓存,并通过按形状键控的 LRU 缓存把每个 prefill/decode 微批作为一张计算图提交,使稳态 decode 重放同一张已分配(在 CUDA 上还已捕获)的图。提示长度不到索引器 top-k 时会跳过稀疏选择直接走稠密注意力,因此短提示不为 DSA 付出任何代价;显存允许时TS_GLM_UBATCH=2048可以调大 prefill 微批(3× RTX PRO 6000 上 pp2048 从 918.9 提到 1145.8 tok/s,该数字出自 GLM-5.2 UD-IQ2_XXS;GLM-5.3 自己的实测数字来自另一台机器 —— 8× A40)。 - Muse Spark 整模型执行器 —— 在
ggml_cuda与ggml_vulkan上,TensorSharp 自有的执行器自己加载分片 GGUF,每个微批提交一张 GGML 图,由ggml_backend_sched在所有 GPU 与 CPU 之间切分,底层是未经修改的上游 ggml。计算图按形状缓存,decode 步复用自己的图,prefill 以 2048 token 为一个微批。--layer-split N把每层的稠密权重留在该层所在的 GPU 上,TS_MUSE_SPARK_CPU_MOE=auto则逐个放置该层的 gate、up、down 专家堆栈,让留在主机上的字节尽量少——这部分字节决定了 decode 的成本。在多路 CPU 主机上,主机侧专家位于 NUMA 本地内存并由绑核的工作线程执行,2–16 token 的批次走 TensorSharp 自有的静态行内核,结果与 ggml 逐位一致。全部专家都在 CUDA GPU 上时,按层切分会流水线式 prefill:一个常驻调度器把每次提示调用切成按调用决定大小的微批(约为调用的四分之一,介于 512 与 2,048 行之间;TS_MUSE_SPARK_PIPELINE_UBATCH可固定大小),GPU k 处理一个微批时 GPU k+1 处理前一个;TS_MUSE_SPARK_PIPELINE=0将其关闭。在ggml_cuda上,--tp N则在上游 ggml 的 meta 后端上构建同一张图,由 TensorSharp 的切分方案把每一层分到 N 张 GPU 上——注意力按 KV 组、路由专家沿隐藏维度切分——每层两次 all-reduce。TS_MUSE_SPARK_NATIVE=0选择逐算子路径(十张 A40 上的实测)。 - Nemotron-H 设备侧 decode 注意力 —— 在 GGML 后端上,注意力层直接用 flash-attention 内核对常驻 KV 缓存做 decode,decode 速度不再随上下文长度衰减。
- 保留 E4B 语义的融合 prefill / verify —— 原生整模型路径会在 E4B prefill/verify 中携带逐层嵌入(PLE)与共享 KV donor 映射,而不会回退到数百次逐 op 提交。
- 调度器自动 N=1 快路径 —— 只有一个序列被调度时,服务器会自动使用 Gemma 4 的融合逐序列 forward,避免完全批处理路径的固定开销。
- 分块 prefill(Gemma 4)—— 长提示被切成有界块,避免滑动窗口层产生 O(n²) 的注意力分数张量。独占请求使用
TS_SCHED_SOLO_PREFILL_CHUNK(默认 8192);混合 prefill+decode 步使用TS_SCHED_PREFILL_CHUNK(默认 256),仅 prefill 时则公平用满设备 token 预算。 - 融合 Qwen 3.5/3.6 注意力与 FFN —— 单图融合注意力层 decode、融合 prefill 注意力、融合 out-proj + FFN,以及融合视觉编码器块(约 15 op → 2)。CUDA 与 Metal 的并发 decode 会共享一张槽位稳定的 token 批处理图,不再为每个请求分别扫描一次权重。
- 融合 QK-Norm + RoPE(Qwen 3.5/3.6,direct CUDA)——
cuda后端在纯文本 prefill 路径用单个 CUDA kernel 完成 QK-Norm 与 NeoX RoPE。默认开启;用TS_FUSED_QKNORM_ROPE=0禁用。 - 原生量化计算 —— Q4_K_M、Q6_K、Q8_0、IQ2_XXS、MXFP4 直接用于 matmul 而不展开为 FP32;批量
AddmmQuantBatch在一次分派中处理多个子权重 matmul。 - 批量 GPU MoE —— 所有被选专家(加上可选的共享专家与残差相加)在每个 MoE 层折叠为一次 GGML 图分派。
- KV 缓存前缀复用 —— 多轮对话复用最长匹配 token 前缀;滑动窗口模型按窗口大小回退截断。
- 内核预热 —— CLI 与服务器都在启动时运行一次很小的前向,以预编译 GPU 内核并预热内存池,避免冷启动延迟。
内存优化
- 零拷贝文件映射权重 —— GGUF 被内存映射,量化张量直接绑定进原生运算,去掉了大致使常驻内存翻倍的逐张量拷贝。例如
Qwen3.5-35B-A3B-IQ2_XXS(约 10 GB GGUF)在 Metal 下峰值约 7 GB,而非约 17 GB。 - 最佳匹配内存池,带有界保留(块上限 64 MB,池上限 32 块),让长时间运行中工作集保持紧凑。
- 分页 KV 块池 —— 块取自同一个共享池;池中空闲块用尽时,radix 缓存按保护层级淘汰其为复用而保留的前缀块,每一层内按最近最少使用的顺序,因此最近用过的共享公共前缀(如系统提示)最后才被淘汰。
- KV 缓存精度 —— 缓存的元素类型由
--kv-cache-dtype(f32、f16、q8_0、q4_0)设置;DeepSeek V4 与 V4.1 都拒绝块量化类型。Muse Spark 的原生执行器使用 F16 的按序列槽位,它的逐算子路径同样拒绝块量化类型。 - 上下文按实际空闲的显存来定 —— 自报的上下文是上限而不是请求。GLM-5.2 的 GGUF 宣称 1,048,576 token,那是约 93 GiB 的 KV,所以权重落盘之后,加载器会去问每张卡还剩多少,按「缓存加一整个 prefill 计算图」能装下的大小定上下文,并打印它的选择:3 卡按层切分 342,272 token,
--n-cpu-moe 30646,400,--tp 391,136。设MAX_CONTEXT则把某个长度变成硬性要求——放得下就照办,放不下会带着数字直接报错。