高级功能
让 TensorSharp 既快又可扩展的系统:带分页 KV 缓存的连续批处理、推测解码、文本扩散,以及底层的内核级与内存级优化。
连续批处理与分页 KV 缓存
服务器的 InferenceEngine 是一个 vLLM 式连续批处理引擎,默认开启。它不再一次只运行一个请求,而是以单个 decode 步的粒度将多个请求交错运行。
- 分页 KV 池 —— KV 缓存被划分为取自共享池的定长块,因此内存按块分配,而非按最坏情况的序列长度分配。
- 块哈希前缀共享 —— 每个完整块都做内容哈希;相同前缀(系统提示、共享上下文)在并发与顺序请求间共享,而非重新计算。
- 迭代级调度器 —— 在批中接纳与抢占序列,并在实现
IBatchedPagedModel的模型上把它们打包进一次前向。 - 可选 SSD 层 —— 对于很大的 KV 工作集,冷块可溢出到 SSD 层。
尚未实现批量路径的模型仍运行在引擎隔离的逐序列 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)上,它比 legacy 逐序列 GGML 路径快约 21%。
批量前向(Mistral 3、Gemma 4、GPT OSS、带 GatedDeltaNet 循环状态池的 Qwen 3.5/3.6,以及带 Mamba2 循环状态池的 Nemotron-H)把 N 个序列打包进一次 ForwardBatch 调用,每层一次批量线性投影 matmul,并配以分页 K/V 散写。Gemma 4 达到约 1.5–1.6× legacy 吞吐;Nemotron-H Mamba2 批量在 Apple M4 Pro 上 batch=3 时达到约 3.95×。
完整深入解析:仓库中的 docs/PAGED_ATTENTION_AND_CONTINUOUS_BATCHING.md。
MTP / NextN 推测解码
部分架构自带一个多 token 预测(MTP / NextN)草稿头,让服务器可为单序列(非并发)运行无损推测解码。草稿廉价地提出若干未来 token,主干在一次批量前向中验证全部,被接受的 token 一步提交。
由于请求自身的采样器 —— 温度、top-k/p 与全部惩罚 —— 同时驱动草稿与验证,输出与标准解码完全一致。推测只改变产生相同 token 所需的前向次数。
它默认关闭。在服务器上用 --mtp-spec(环境变量 TS_MTP_SPEC=1)启用:
# Qwen 3.6 —— 必须使用保留 NextN 块的 -MTP- 仓库 GGUF;基础仓库导出会剥离该块
dotnet run --project TensorSharp.Server -c Release --no-build -- --model Qwen3.6-35B-A3B-UD-IQ2_XXS.gguf --backend ggml_cuda \
--mtp-spec --mtp-draft 8 --mtp-pmin 0.75
# Gemma 4 —— 加载与目标匹配的独立 gemma4-assistant 草稿 GGUF
dotnet run --project TensorSharp.Server -c Release --no-build -- --model gemma-4-12B-it-qat-UD-Q4_K_XL.gguf --backend ggml_cuda \
--mtp-spec --mtp-draft-model mtp-gemma-4-12B-it.gguf
两种草稿头形态
- Qwen 3.6(内嵌 NextN) —— GGUF 携带一个额外解码块以及 NextN 投影/归一化张量。无需独立文件;
--mtp-draft-model会被忽略。只有 unsloth/Qwen3.6-35B-A3B-MTP-GGUF 一类保留 NextN 张量的导出才具备该块;基础仓库导出会剥离它并静默回退到标准 decode。GatedDeltaNet 主干状态会被快照,以便部分被拒绝的验证批可回滚。 - Gemma 4(独立
gemma4-assistantGGUF) —— 一个 EAGLE 式循环草稿器,用--mtp-draft-model加载。它自身不持有 K/V:每个草稿层都查询目标模型已有的逐层 KV 缓存。草稿的隐藏维度必须与目标一致(把 12B 目标与其 12B 草稿配对)。不匹配或不完整的草稿会在启动时立即失败并给出修复提示。
何处有收益
| 后端 | Qwen 3.6 | Gemma 4 |
|---|---|---|
| GGML CUDA / GGML Metal | ✅ 融合验证 + 草稿内核 | ✅ 融合验证 + 草稿内核 |
直接 CUDA(cuda,纯 C#) | ✅ GPU 常驻逐 op 验证/草稿 | ✅ GPU 常驻逐 op 验证/草稿 |
| CPU / GGML CPU / MLX | 标准解码 | 标准解码 |
调参:--mtp-draft(默认 8)限定每步草拟的 token 数;--mtp-pmin(默认 0.75)是保留某 token 所需的草稿头最低置信度。Gemma 4 的 A/B 开关是 TS_GMTP_* 环境变量。
DiffusionGemma 文本扩散
DiffusionGemma 与自回归模型本质不同:它不逐 token 调用 Forward()。相反,它在 Gemma-4 衍生的 MoE 主干上,对定长画布做块式 EntropyBound 去噪 —— 整个答案是被迭代精炼,而非从左到右写出。
- CLI ——
--diffusion-steps(每块去噪步数)、--diffusion-seed与--diffusion-blocks。 - Web UI —— 流式发送整条消息的
replace事件,让你实时观看答案去噪,并通过DiffusionBatchScheduler在块边界批处理并发扩散请求。 - 优化 —— 在 GPU 后端上,
[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×)。
- 保留 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)走融合整图路径;有竞争时降至TS_SCHED_PREFILL_CHUNK(默认 1024)。 - 融合 Qwen 3.5/3.6 注意力与 FFN —— 单图融合注意力层 decode、融合 prefill 注意力、融合 out-proj + FFN,以及融合视觉编码器块(约 15 op → 2)。
- 融合 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 块),让长时间运行中工作集保持紧凑。
- 带可选 SSD 溢出的分页 KV 块池 —— RAM 上限、LRU 淘汰,并跨会话做内容哈希前缀复用。
- KV 块编解码器 —— 通过
--paged-kv-quant-bits用TurboQuantKvCodec(Q2 / Q4 / Q8)做可选的原地压缩,以很小的精度代价换取每块占用减半 / 减至四分之一。