EXL3 现已支持 ninfer-ext
摘要
ninfer-ext 是 NInfer 的扩展分支,新增了对更大 Qwen 模型的支持、更快的推测式解码、Agent 服务功能,以及针对 Qwen3.8-27B 在 NVIDIA RTX 5090 上的 EXL3 量化支持。
查看缓存全文
缓存时间: 2026/09/30 08:25
giveen/ninfer-ext 来源:https://github.com/giveen/ninfer-ext
ninfer-ext
ninfer-ext 是 Neroued/ninfer(https://github.com/Neroued/ninfer)的扩展分支,Neroued/ninfer 是一个为 Qwen 模型编写的、在单张 NVIDIA GeForce RTX 5090 上运行的从零实现的 C++/CUDA 推理引擎。该分支在上游引擎的基础上增加了四项能力:
- 在单张 32 GB GPU 上运行 Qwen3.8-Flash-Next。 这是一个约 180B 参数的 MoE 模型,其路由专家存放在固定内存(pinned Host memory)中,并由设备端专家缓存作为支撑。原版 NInfer 无法加载它。
- 在 Qwen3.8-27B
groupwise-int上更快的投机解码。 与原版在相同产物上的对比中,DFlash2 在 1–4 个并发请求下快 13–48%,MTP 快 8–32%。 - 面向长时间运行的 Agent 的服务能力。 上下文缓存的抢救与锚定(salvage and anchoring)、统一的 Host 层预算、OOM 恢复,以及若干协议扩展。
- Qwen3.8-27B 的 EXL3 量化。 一种格码(trellis-coded)格式,提供 4.0 和 3.5 bpw,配有专用的 C++/CUDA 量化器和算子内核(EXL3 量化)。它产出的产物是本分支所有 Qwen3.8-27B 构建中体积最小的,并且在困惑度(perplexity)和 KL 散度两项指标上都是最优;上游没有任何东西能生成或运行这些产物。
它并非在所有场景下都比原版更快。Qwen3.8-27B nvfp4 和 Qwen3.6-35B-A3B 与原版打平,而在两种场景下原版领先。与原版 NInfer 的对比列出了双方数据。
其余部分均来自上游:架构、.ninfer v3 产物格式、官方产物,以及兼容 OpenAI 与 Anthropic 的服务器。该分支跟踪上游的 master(当前为 bace20dc,即 upstream remote),其代码树已移植到 C++23。
快速上手
1. 环境要求
- 64 位 Linux 系统和一张 RTX 5090(
sm_120a)。不支持其他 GPU。 - 支持
sm_120a的 CUDA toolkit。CUDA 13.3 已经过验证。 - CMake 3.28 或更新版本、支持 C++23 的宿主编译器、Ninja 和
pkg-config。 - FFmpeg 开发库(
libavformat、libavcodec、libavutil、libswscale)以及libcurl >= 7.85。 - 仅 Qwen3.8-Flash-Next 需要:约 128 GB 主机内存和 127 GB 磁盘空间(详情)。
2. 构建
git clone https://github.com/giveen/ninfer-ext.git
cd ninfer-ext
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
这会构建出 build/apps/ninfer-serve(HTTP 服务器)、build/apps/ninfer(一次性 CLI)和 build/apps/ninfer-perplexity。项目没有 install 目标,因此请直接从构建目录运行。cmake --preset dev 还会构建测试和基准测试(构建配置)。
3. 获取模型
上游的官方 v3 产物可直接使用,无需修改:
| 模型 | 权重 | 产物 | 下载 |
|---|---|---|---|
| Qwen3.8-27B | nvfp4 | qwen3_8_27b_nvfp4.ninfer | neroued/Qwen3.8-27B-nvfp4-NInfer(https://huggingface.co/neroued/Qwen3.8-27B-nvfp4-NInfer) |
| Qwen3.8-27B | groupwise-int | qwen3_8_27b.ninfer | neroued/Qwen3.8-27B-NInfer(https://huggingface.co/neroued/Qwen3.8-27B-NInfer) |
| Qwen3.8-27B | exl3 4.0 bpw | qwen3_8_27b_exl3_4bpw.ninfer | jabbatheduck/ninfer-ext-models(https://huggingface.co/jabbatheduck/ninfer-ext-models) |
| Qwen3.8-27B | exl3 3.5 bpw | qwen3_8_27b_exl3_3p5bpw.ninfer | jabbatheduck/ninfer-ext-models(https://huggingface.co/jabbatheduck/ninfer-ext-models) |
| Qwen3.6-27B | nvfp4 | qwen3_6_27b_nvfp4.ninfer | neroued/Qwen3.6-27B-nvfp4-NInfer(https://huggingface.co/neroued/Qwen3.6-27B-nvfp4-NInfer) |
| Qwen3.6-27B | groupwise-int | qwen3_6_27b.ninfer | neroued/Qwen3.6-27B-NInfer(https://huggingface.co/neroued/Qwen3.6-27B-NInfer) |
| Qwen3.6-35B-A3B | groupwise-int | qwen3_6_35b_a3b.ninfer | neroued/Qwen3.6-35B-A3B-NInfer(https://huggingface.co/neroued/Qwen3.6-35B-A3B-NInfer) |
hf download neroued/Qwen3.8-27B-nvfp4-NInfer qwen3_8_27b_nvfp4.ninfer --local-dir models
hf download jabbatheduck/ninfer-ext-models qwen3_8_27b_exl3_4bpw.ninfer --local-dir models
表格中两行 exl3 是本分支自行量化、发布在 Hugging Face 上的产物,可直接下载并运行(EXL3 量化)。
另有两个产物需要自行转换:带 DFlash2 权重的 Qwen3.8-27B(说明)和 Qwen3.8-Flash-Next(说明)。转换后的产物内嵌本分支的聊天模板(转换指南)。已有的 v2 下载可以在本地升级。
4. 启动服务并发送请求
./build/apps/ninfer-serve models/qwen3_8_27b_nvfp4.ninfer \
--max-context 32768 --kv-capacity auto --max-concurrency 2 \
--spec mtp --draft-tokens 5 --fixed-draft --lm-head-draft
curl http://127.0.0.1:8080/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model": "qwen3.8-27b", "messages": [{"role": "user", "content": "Reply with one short sentence."}], "max_tokens": 64}'
服务器支持 OpenAI Chat Completions 与 Responses,以及 Anthropic Messages,包括流式输出和工具调用。--model-id 可修改其报告的模型名;--host 0.0.0.0 可将其暴露到本机之外,--api-key 则要求请求携带密钥。
不启动服务器、直接得到一次性回答:
./build/apps/ninfer models/qwen3_8_27b_nvfp4.ninfer \
--prompt "Explain prefill and decode." \
--max-context 32768 --spec mtp --lm-head-draft
回答输出到 stdout,诊断信息和耗时报告输出到 stderr。CLI 指南、HTTP 服务指南和示例覆盖了所有选项;每个二进制的 --help 都会列出当前可用的参数。
Docker。 执行 docker build --tag ninfer:local .,然后将带 --host 0.0.0.0 的 ninfer-serve 命令连同挂载好的模型目录一起传给 docker run --gpus all。
推荐设置
以下是按模型测得的最快设置。“并发”指同时有多少个请求在解码;请将 --max-concurrency 设为该数字,因为每增加一个并发槽位,无论是否被使用,都会预留 KV 和计算图内存。
表中标注“由负载决定”的单元格,其背后的数据见性能和与原版 NInfer 的对比。
| 模型 | 1 个请求 | 2–4 个并发 | 8 个并发 |
|---|---|---|---|
Qwen3.8-27B nvfp4 | --spec mtp --draft-tokens 5 --fixed-draft --lm-head-draft | 相同 | 相同 |
Qwen3.8-27B groupwise-int(DFlash2 权重) | --spec dflash2 --draft-tokens 7 --lm-head-draft | 相同 | 相同 |
Qwen3.8-27B groupwise-int(官方产物) | --spec mtp --draft-tokens 5 --fixed-draft --lm-head-draft | --spec mtp --lm-head-draft | --spec mtp --draft-tokens 5 --fixed-draft --lm-head-draft |
Qwen3.8-27B exl3 4.0 / 3.5 bpw | --spec mtp --draft-tokens 3 --fixed-draft | 未测量 | 未测量 |
| Qwen3.6-35B-A3B | --spec mtp --lm-head-draft | 2 个请求时同上;4 个请求时,长推理同上,短散文不加 --spec | 长理选用 --spec dflash --draft-tokens 7 --lm-head-draft;短散文不加 --spec |
| Qwen3.6-27B(两种) | 未测量;请从 Qwen3.8-27B 中相同权重的那一行起步 | ||
| Qwen3.8-Flash-Next | 不加 --spec | 不加 --spec | 不加 --spec |
理由如下:
- 27B 模型。 投机解码在任何并发下都有收益。在长推理负载上,
nvfp4上固定 5 token 的 MTP 草稿比自适应默认值快 3–17%。在groupwise-int上,2–4 个请求时自适应默认值更优,1 个和 8 个请求时固定 K=5 更优。只要产物带有 DFlash2 权重,DFlash2 就是groupwise-int上最快的模式。 - EXL3。 目前只测量了单请求设置:固定 3 token 的 MTP 草稿,与其他 27B 产物使用的投机形态相同。并发场景尚未测量。
- 35B-A3B。 一到两个请求时,自适应 MTP 在长推理上与最佳固定草稿相差 5% 以内,在短提示上则领先。从 4 个请求起,投机解码对短散文反而有害:在 512 token 的作文上,纯解码在 C=4 时比 MTP 快 14%,C=8 时快 32%。在 C=8 的长推理上,DFlash 配 7 个草稿是最快的模式。
- Flash-Next。 解码速度受限于通过 PCIe 拉取专家。一次验证轮最多路由四列,触及的专家数量超过被接受草稿所节省的量。纯解码在单请求下比 MTP 快 26%,C=8 时快 6%。
- 自适应默认值(仅
--spec mtp) 在负载未知或混合时是合理选择,详见自适应 MTP 草稿长度。
对速度有影响的其他设置:
- 保持 CUDA Graphs 和前缀复用开启。 两者均为默认行为;
--no-cuda-graph和--no-prefix-reuse仅供调试和基准测试使用。 - 按实际需要设置
--max-context。 KV 池与其它内容一样占用同一块显存。在 Flash-Next 上它直接从专家缓存中划出,缓存越小意味着 PCIe 拉取越多。 --kv-dtype fp8可将长上下文的 KV 内存减半。在 Flash-Next 上,2,048 token 的提示之后它还能快 3%;其他模型在本项目中尚未测量其速度影响。- 会重发长历史的 Agent 循环: 用
--host-cache-mib(例如16384)给 Host 缓存层留出空间,使可复用前缀能保留在固定内存中。 - Flash-Next: 保持
--expert-cache auto(默认值),并确保有足够的空闲主机内存,使 n-gram 表维持在页缓存映射状态(由--ngram-residency auto决定)。 - 上下文超过显存容量: 增加
--kv-stream;参见 KV 流式传输。
示例:面向四个长上下文 Agent 的 Qwen3.8-27B nvfp4:
./build/apps/ninfer-serve models/qwen3_8_27b_nvfp4.ninfer \
--max-context 131072 --kv-capacity auto --max-concurrency 4 --kv-dtype fp8 \
--host-cache-mib 16384 \
--spec mtp --draft-tokens 5 --fixed-draft --lm-head-draft --preserve-thinking
KV 流式传输
KV 流式传输让单个请求的上下文可以超出显存中 KV 缓存的容量。每个上下文中最近的部分保留在 GPU 上;更早的、以 64 token 为单位的完整页会转移到固定主机内存中,注意力算子内核从那里读取。这样一来,上下文长度由 --max-context(最多不超过模型的 max_position_embeddings)以及你为 Host KV 层分配的内存决定,而不再受限于显存。
工作原理
- 每个活跃请求拥有均等的 GPU KV 份额,即
--kv-capacity / --max-concurrency。它可以借用空闲的份额,并在其他请求需要时归还。 - 某请求超出其 GPU KV 配额后,会把最旧的完整页迁移到 Host KV(
--host-kv-mib,或针对整个 Host 层使用--host-cache-mib)。最前几页和最近的尾部始终留在 GPU 上。 - 每一步都会按层将被溢出的页复制到 GPU 上,并与上一层的计算重叠执行。在 Flash-Next 上,稀疏注意力只从内存读取选中的 token,并在 GPU 上保留一份注意力索引。
- 准入时会为每个请求的提示加上输出上限、超出其配额的部分预留 Host KV。放不下的请求需等待正在运行的请求完成;永远放不下的请求会收到 HTTP 400。
- 上下文缓存仍然有效:后续轮次、分支和共享前缀会就地复用已溢出的 KV,而不会重新做 prefill。
用法
加上 --kv-stream,并按溢出量设置 Host KV 层的大小:
./build/apps/ninfer-serve models/qwen3_8_27b_nvfp4.ninfer \
--max-context 262144 --kv-capacity auto --max-concurrency 2 --kv-dtype fp8 \
--kv-stream --host-kv-mib 65536
- Host KV 大小: 每个请求可以溢出(提示 +
max_tokens− 配额)个 token 的 KV。每个 token 的 KV 字节数为全注意力层数 × KV 头数 × 头维度 × 2(K 与 V)× 每个元素的字节数;若启用 MTP,则还要加上 MTP 层。请按实际并发的溢出量来设定--host-kv-mib。 - KV dtype: 与 BF16 相比,
--kv-dtype fp8可将每个溢出步骤读取的字节数减半。 --kv-capacity: 份额越大,每个上下文保留在 GPU 上的部分越多,溢出越少。- GPU 开销: 两个暂存缓冲区各自容纳完整
--max-context的一层 KV,在 262k 上下文时使用 FP8 约共 1 GB,使用 BF16 约共 2 GB。 - 固定内存: Host KV 在引擎整个生命周期内保持固定(pinned)状态,请为操作系统和页缓存留出空间。
性能
能放进其 GPU 份额的请求以常驻速度运行。一旦请求发生溢出,每一步都要通过 PCIe 读取其溢出的 KV:
| 负载(上下文 / GPU 份额) | 流式 | 常驻 |
|---|---|---|
27B nvfp4,BF16 KV,35k / 12k,解码 | 31 tok/s | 71 tok/s |
| Flash-Next,80k 提示 / 12k 份额,解码 | 41–44 tok/s | 46–51 tok/s |
| 27B 对话,在已溢出的 30.6k 上下文上的第二轮,TTFT | 188 ms | 170 ms(全新 prefill:4.2 秒) |
两种情况下 prefill 都以常驻速率运行,流式输出与常驻运行结果一致。稠密模型的解码受主机到 GPU 的拷贝带宽(约 50 GB/s)限制,因此会随着上下文溢出比例增大而变慢;Flash-Next 几乎不变慢,因为它只读取选中的 token。
限制
- DFlash 与 DFlash2 的草稿生成以及离线评分(
ninfer-perplexity)不支持--kv-stream。 - 在 27B 上测试到两个并发的约 236k token 的 FP8 请求,在 Flash-Next 上测试到 80k token。设计细节见 Paged KV cache §6.5。
与原版 NInfer 的对比
上游原版 bace20dc 与本分支(引擎为 19f38b77)在相同的 RTX 5090 上、使用相同的产物、在同一套测试框架下运行。
方法。 上游的解码饱和度套件(tools/bench/run_serve_concurrency.py):
- KV 与采样: INT8 KV,以及上游的随机采样配置。投机模式统一使用
--lm-head-draft。 - 负载: C 个并发请求,每个请求从一条长推理(AIME)提示生成 4,096 个 token。
- 指标: 在解码批次等于 C 的区间内,稳态解码 tok/s。
- 重复次数: 每个数值是两次运行的平均值。单次运行可能波动约 10%,因此差异在约 5% 以内视为打平。
- 调度: 原版与分支在每轮内交替运行,唯一例外是分支的固定 K=5 运行,它们作为独立会话稍后执行。
尚未重新测量。 分支的两个后续提交会影响下表数据:NVFP4 MLP 投影的 tensor-core A16 内核(e348a5d2,在仅分支运行中使 27B nvfp4 的 MTP 提升 5–9%)和自适应 MTP 批量草稿长度(52b06393)。下表早于这两个提交。
ninfer-ext 领先的场景
每个单元格给出分支的聚合解码 tok/s,然后是相对原版在该并发下最佳模式的变化幅度。
| 产物 | ninfer-serve 参数 | C=1 | C=2 | C=4 | C=8 |
|---|---|---|---|---|---|
Qwen3.8-27B groupwise-int + DFlash2 | --spec dflash2 --draft-tokens 7 --lm-head-draft | 260 +40% | 403 +48% | 522 +13% | 727 +3% |
Qwen3.8-27B groupwise-int,仅 MTP | C=1 和 C=8:--spec mtp --draft-tokens 5 --fixed-draft --lm-head-draft | 203 +22% | 658 +8% | ||
C=2 和 C=4:--spec mtp --lm-head-draft | 308 +29% | 520 +32% | |||
Qwen3.8-Flash-Next nvfp4 | 参见 Qwen3.8-Flash-Next | 仅分支 | 仅分支 | 仅分支 | 仅分支 |
作为参考,原版的最佳成绩:DFlash2 为 186 / 271 / 462 / 705 tok/s,仅 MTP 为 167 / 239 / 395 / 607 tok/s(C=1–2 时 K=3,C=4–8 时 K=5)。原版在加载 Flash-Next 产物时报 tensor: unknown member divisors 错误。
groupwise-int 的增益与已合并的 Q5 K-split MMA 路径(上游 PR #292)在小批量下的效果一致,该路径针对的是投机解码的验证轮。NVFP4 产物不含 Q5 权重,因此与原版打平。该归因尚未通过 A/B 实验独立验证。
原版领先或打平的场景
| 产物 | 场景 | 原版 | 分支 | 变化 |
|---|---|---|---|---|
Qwen3.8-27B groupwise-int |
相似文章
NInfer 对 Qwen3.8-27B 的 Day-0 支持:约200 tok/s 生成速度,以及大量引擎改进
NInfer 新增对 Qwen3.8-27B 模型的 Day-0 支持,在单张 RTX 5090 上使用推测解码可实现约 200 tok/s 的生成速度,并包含并发请求和内核优化等引擎改进。
(NInfer分支) 我为Qwen-3.8 27B在双5090显卡上实现1M上下文的尝试
一位开发者分支了NInfer,这是一个C++20/CUDA推理引擎,添加了张量并行和YaRN绳索缩放,使得Qwen3.8-27B能够在双5090显卡上运行1M token上下文,并在特定解码场景中优于vLLM。
NInfer RTX 4090 for Qwen 3.8 27B 更新 - 最高支持25万-35万令牌上下文在显存中
作者已更新其 NInfer 分叉,添加了 rk2v4-e8 量化选项用于 KV 缓存,使得在单张 RTX 4090 上实现最高25万-35万令牌上下文,无需溢出到系统内存,并进行了优化以提升生成速度。
Ninfer 和 RTX 5090 配合 3.8 27B 模型让我喜极而泣,效果太棒了。
一位用户报告称,使用 Ninfer 工具在 NVIDIA RTX 5090 GPU 上运行 Qwen 3.8B 模型时,获得了高令牌吞吐量,显著优于 llama.cpp。
Qwen3.6-27B 推测解码在更大量化下性能提升
Qwen3.6-27B 模型在使用更大量化级别时,推测解码性能提升,推理效率增强。