Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ninfer-L20

把 NInfer 推理引擎移植到 NVIDIA L20(Ada / sm_89 / 92 SM / 48 GB GDDR6 / 864 GB/s)的补丁集、构建工具与实测数据。

基线是 sergiuszm/ninfer-4090(sm_89 / RTX 4090 移植, 源自 Don-Chad/ninfer-3090 → Neroued/ninfer)。

L20 与 RTX 4090 同为 sm_89,所以这是同代移植;差别在 SM 数、带宽与显存:

RTX 4090 L20
SM 数 128 92(−28%)
带宽 1008 GB/s 864 GB/s(−14%)
显存 24 GB 48 GB(+100%)
架构 sm_89 sm_89

这两条差异决定了移植的全部内容:网格尺寸必须按 92 SM 校准;KV/并发 profile 应当放宽而不是收紧。


实测结果

单请求,L20,官方 groupwise 产物(16.96 GiB)+ INT8 KV @ 262144 + --prefill-chunk 1024:

用例 采样 tok/s 草稿接受率 TTFT
code 贪心 100.11 65.5% 209 ms
qa 贪心 99.98 65.1% 191 ms
qa temp0.7 91.39 56.7% 63 ms
code temp0.7 82.63 48.9% 63 ms
prose 贪心 79.91 45.2% 203 ms
prose temp0.7 78.34 43.8% 63 ms
全部(--spec none 对照) — 38.9 – 39.3 — —

MTP 加速比:code 贪心 2.57× / code temp0.7 2.12× / prose 2.05×。 显存:开 MTP 27,509 MiB,不开 26,197 MiB。

与上游 4090 参考值对照

指标 RTX 4090(上游 README) L20 实测 占 4090
无投机 50.5 39.0 77%
code MTP3 142.9 – 148.6 100.11 68 – 70%

L20 的带宽是 4090 的 85.7%、SM 数是 71.9%,实测落在 68–77% —— 与硬件规格吻合,移植没有引入额外损失。

与 llama.cpp 的同类对比(同一张 L20)

引擎 产物 权重 无投机 code MTP3
llama.cpp Q6_K_XL GGUF 24.13 GiB 25.45 59.58
NInfer-L20 container-v2 .ninfer 16.96 GiB 39.0 100.11

两者不是同一量化,所以 1.68× 的差距里混着"产物更小"和"引擎更强"两部分。按字节归一化才能分离引擎贡献:

引擎 每 token 字节 ms/token 有效带宽 占 864 GB/s
llama.cpp 25.91 GB 39.3 659 GB/s 76%
NInfer-L20 18.21 GB 25.6 710 GB/s 82%

→ 同为 Ada、同一张卡,NInfer 的引擎效率高约 8%(与上游自称的 10% 同向)。


移植改了什么

补丁共 23 个文件,全部在 patches/l20-port.patch, 由 patches/apply-l20-port.ps1 幂等应用。

补丁 内容 性质
P1 顶层 CMakeLists.txt 按架构定义 NINFER_SM86 / NINFER_SM89 架构身份
P2 删除 src/CMakeLists.txt 里硬编码的 NINFER_SM86=1 架构身份
P3 16 个文件里 28 处兼容类开关改为 SM86 || SM89(行为保持) 架构身份
P4 core/device.h:新增 device_sm_count() 声明 运行时宽度
P5 core/device.cu:device_sm_count() 实现(查询一次并缓存) 运行时宽度
P6 gdn/chunked/output.cu:波尺寸从硬编码 170 SM 改为运行时 device_sm_count() 真缺陷 A
P7 sparse_moe/prefill:同类修正(仅 35B-A3B 路径) 同类缺陷
P8 bf16_gdn_gating_proj_kernels.cu:split4/2 的协同驻留数 2 → 1 真缺陷 C(L20 真实崩溃)

P8:L20 上唯一真正的崩溃

ninfer_gdn_gating_proj_test .........  Subprocess aborted
bf16_gdn_gating_proj_kernels.cu:310: CUDA_CHECK(cudaLaunchKernelEx(...)) failed:
cudaErrorCooperativeLaunchTooLarge: too many blocks in cooperative launch

根因:cooperative_resident_ctas_per_sm<Bf16Gdn27Geometry, SplitK>() 对所有 SplitK 都返回 2, 但只有 split8 是 8 warps(256 线程 / 65 寄存器),split4、split2 走默认 kBf16GdnWarps = 16 (512 线程 / 74 寄存器):

SplitK 线程 寄存器 每 CTA ×2 Ada 寄存器文件 65,536
split8 256 65 16,640 33,280 放得下 → 2 CTA/SM
split4 / split2 512 74 37,888 75,776 超了 → 只能 1 CTA/SM

该常量被发射钳制读取,用来决定单次协同发射覆盖多少 token tile。高估 → 网格超出真实驻留容量。 92 SM 上 split2 算出 max_token_tiles = 92×2 ÷ 6 = 30,T=2688 单次发射生成 126 CTA 网格, 真实容量只有 92 → 驱动拒绝。

为什么只在 L20 暴露:4090 有 128 SM,同样的高估(128×2 = 256)刚好盖住测试的网格规模。

关键旁证:bf16_gdn_gating_proj_plan.cpp 的 resident_ctas_27() 本来就是对的 (split8→2、split4/2→1)。两个文件互相矛盾,而只有内核文件那个常量被发射钳制真正读取—— 静态断言读的是对的那个,所以问题一直没被发现。

修复是一行:return SplitK == 8 ? 2 : 1;

修复前 修复后
ninfer_gdn_gating_proj_test Subprocess aborted Passed 1.75 s
全套件可运行测试 98 中 1 失败 98 全通过 / 0 失败

P6:GDN 输出内核按 RTX 5090 的 170 SM 定网格(热门路径)

constexpr std::int64_t kRtx5090SmCount = 170;
constexpr std::int64_t kTargetCtas     = kRtx5090SmCount * kCtasPerSm;   // 680

L20 真实单波是 92 × 4 = 368。目标值偏大 → jobs_per_block 偏大 → 网格偏窄 → 每 CTA 串行更多。 而且 jobs_per_block == 1 决定 launch_fixed<false> / <true>(MULTI_JOB 特化), 目标值错了会连内核特化一起选错。该路径在 prefill 时每个 GDN 层都跑,而 Qwen3.8-27B 的 65 层里有 49 层是 GDN。

P3:sm_89 构建被定义为 NINFER_SM86

需要精确说明危害边界:w8_config.h 那个分支是 compat 调度,而 Ada 本来就该走 compat 分支 (#else 是 Blackwell 专用);pdl.cuh 的 SM86 分支是"无 PDL 的普通发射",Ada 也确实没有 PDL。 所以它当前不改变生成代码,不是性能 bug。

真正的危害是架构身份失效:任何将来为 Ampere 添加的 NINFER_SM86 专用调优会静默作用于 Ada, 而 w8_config.h 恰恰管着 MTP 投影的 W8 GEMM,也就是直接卡在解码关键路径上。


构建

需要 WSL2 + Ubuntu 24.04(22.04 不可用,见下)、CUDA ≥ 12.8、GCC 13、CMake ≥ 3.28。

# 1) 依赖(脚本会把项目的真实版本下限做成硬门禁)
bash scripts/wsl-install-deps.sh

# 2) 克隆上游 + 打补丁 + 配置 + 编译
NINFER_L20_REPO=/mnt/d/ninfer-L20 bash scripts/wsl-build.sh

wsl-build.sh 会 clone sergiuszm/ninfer-4090@rtx4090-port 到 /opt/ninfer-L20/src、 git apply 本仓库的补丁、用 Ninja 配置(-DCMAKE_CUDA_ARCHITECTURES=89,GCC 13 作 host compiler), 然后 cmake --build -j。

源码放 WSL 原生文件系统(/opt),不放 /mnt/d——跨 9p 做 1400 文件的 CUDA 构建会慢很多倍。

⚠️ Ubuntu 22.04 无法构建

顶层 CMakeLists.txt 的下限是写死的:

pkg_check_modules(FFMPEG REQUIRED IMPORTED_TARGET
  libavformat>=60 libavcodec>=60 libavutil>=58 libswscale>=7)
pkg_check_modules(LIBCURL REQUIRED IMPORTED_TARGET libcurl>=7.85)

22.04 的 FFmpeg 停在 4.4(libavformat 58.76)、libcurl 7.81,一次 configure 就失败。 24.04 原生满足全部:FFmpeg 6.1(60/60/58/7)+ libcurl 8.5 + GCC 13 默认。

本机没有 CUDA toolkit 时

CUDA toolkit 装在某个 WSL 发行版里、而构建要在另一个发行版里做,可以用共享的 /mnt/wsl (16 GB tmpfs)搬运,实测 4.9 GB 只需几秒:

# 在已有 toolkit 的发行版里
bash scripts/cuda-stage-2204.sh
# 在目标发行版里
bash scripts/setup-2404.sh

运行

bash scripts/ninfer-start.sh 8090

L20 的推荐 profile(脚本内已固化):

ninfer-serve qwen3_8_27b.ninfer \
  --host 0.0.0.0 --port 8090 \
  --max-context 262144 --kv-capacity 262144 \
  --max-concurrency 1 --max-pending-requests 16 --pending-timeout-ms 600000 \
  --prefill-chunk 1024 --kv-dtype int8 \
  --spec mtp --draft-tokens 3 --lm-head-draft

这里体现了 48 GB 的价值:4090 在 24 GB 下 INT8 KV 只能到 172,032 (196,608 启动即被拒),被迫改用 4-bit 的 E8 模式并承担 5.7% 解码税。 L20 的 48 GB 让 INT8 KV 直接开满原生 262,144,精度更高、无额外税。

--prefill-chunk 保持在 2688 以下(超过会路由到 unsplit 调度,onset 精度差约 1e-5)。


模型产物:必须钉住 revision

HF 的 main 已经是 v3 容器,基线 fork 只认 v2,不钉 revision 会直接启动失败:

FATAL server failed during startup | artifact magic is not NInfer v2
revision 提交日期 说明 容器版本 体积 可读
51630a0c0f 2026-09-15 "Publish v3 artifact" ← main 现在指向它 3 19.03 GiB ❌
dc370fb629 2026-09-06 加入 DFlash2 陪伴权重 2 19.03 GiB ✅
3526913004 2026-08-14 初始产物 2 16.96 GiB ✅

rtx4090-port 分支最后推送是 2026-09-12,比 v3 发布早三天——这就是漂移来源。 3526913004 的 18,210,531,328 字节 = 16.96 GiB,正是基线 README 引用的那个体积。

M=https://huggingface.co/neroued/Qwen3.8-27B-NInfer/resolve/3526913004
curl -fsSL -o SHA256SUMS.v2 "$M/SHA256SUMS"
curl -L -C - --retry 8 --retry-all-errors -o qwen3_8_27b.ninfer "$M/qwen3_8_27b.ninfer"
sha256sum -c SHA256SUMS.v2      # eec39564993d6e9c7d5e383382a760f093465c9d163ec9a1bd6b80199514bf3e

用 curl -r 0-7 只读头部 8 字节就能验证任意 revision 的容器版本,无需整包下载。

中国大陆网络下 huggingface.co 可能不可达,把域名换成 hf-mirror.com 即可。


测量

# 引擎侧(NInfer):含无投机对照组
python3 scripts/bench-ninfer-l20.py --model /opt/ninfer-L20/models/qwen3_8_27b.ninfer \
  --kv-dtype int8 --max-context 262144 --spec mtp --draft-tokens 3

# llama.cpp 侧对照:解码 + 多深度扫描
$env:LLAMA_DIR = "C:\path\to\llama.cpp"
.\bench\bench-baseline.ps1
.\bench\bench-mtp-matrix.ps1

bench/results/ 里保留了产生本文所有数字的原始 JSON。

llama.cpp 侧的调优发现

docs/LLAMACPP-BAT-OPTIMIZATION.md 记录了在同一台机器上 对 llama.cpp 启动脚本的实测调优,其中几条与引擎无关、可普遍适用:

  • --no-mmproj-offload 在 48 GB 卡上是净损失:视觉塔钉在 CPU 时 视觉 TTFT 1525 ms → 398 ms、 视觉 prefill 165 → 631 tok/s,代价只有 1106 MiB 显存
  • --threads 要按物理核数给:i9-13900K 是 24 物理核,给 16 会闲置 8 个
  • 每个响应都带隐藏思考:reasoning_effort 只接受 low/medium/xhigh(默认 xhigh), 拒绝 none;要关思考用顶层 {"enable_thinking": false} —— 否则用"答案 token ÷ 总耗时"算速率会远低于引擎实际解码速率

已知限制

  • 未做深上下文扫描。本文所有数字都是浅上下文(prompt 5–81 tokens)。上游数据显示 MTP 接受率 随深度下降(code 81% → 72.9% @128K),L20 的深度曲线需要长 prompt 单独测。
  • P6 的收益未做前后 A/B(预期集中在 prefill)。
  • draft-tokens 最优 K 未扫(本文固定 3;上游姊妹 fork 报 MTP4/MTP7 在深上下文更优)。
  • 35B-A3B 的协同驻留常量未审计:Bf16Gdn35Geometry 返回 SplitK == 32 ? 2 : 4, 而 4 CTA × 512 线程 = 2048 线程/SM 超过 Ada 的 1536 线程/SM 上限,同样可疑。 Qwen3.8-27B 是稠密模型不走该路径,所以未触及。
  • 6 个 *_real_test 未运行(需要真实产物)。
  • 未经上游 review。这是个人移植,不是上游官方支持的平台。

目录

.
├─ patches/
│  ├─ l20-port.patch            23 文件补丁(git apply 可直接使用)
│  └─ apply-l20-port.ps1        幂等补丁脚本(逐处精确匹配,失败即报)
├─ scripts/
│  ├─ wsl-probe.sh              WSL 环境探测
│  ├─ wsl-install-deps.sh       依赖安装 + 项目版本下限硬门禁
│  ├─ cuda-stage-2204.sh        跨发行版搬运 CUDA toolkit(共享 /mnt/wsl)
│  ├─ setup-2404.sh             Ubuntu 24.04 侧接收 + 依赖
│  ├─ wsl-build.sh              克隆 + 打补丁 + 配置 + 编译
│  ├─ ninfer-start.sh           按 L20 profile 启动引擎
│  ├─ bench-ninfer-l20.py       引擎侧基准(含无投机对照组)
│  └─ run-l20-measurement.ps1   端到端测量编排
├─ bench/
│  ├─ bench-baseline.ps1        llama-bench 基线 + MTP 测试
│  ├─ bench-mtp-matrix.ps1      MTP 有效性矩阵
│  ├─ bench-llamacpp-3.8.ps1    多深度解码/prefill 扫描
│  ├─ bench-llamacpp-vision-thinking.ps1  视觉 oracle + 思考开销
│  └─ results/                  原始实测输出
└─ docs/
   ├─ L20-PORT-SPEC.md          逐补丁技术规格与后续工作
   └─ LLAMACPP-BAT-OPTIMIZATION.md  llama.cpp 启动脚本调优实测

致谢与许可

本项目是 Apache-2.0 许可,且是以下 Apache-2.0 项目的衍生作品:

上游源码不在此仓库内分发(见 .gitignore 的 src/)。请自行克隆基线后应用补丁。 改动清单见 NOTICE,符合 Apache-2.0 第 4(b) 条。

About

Port NInfer, a single-GPU CUDA inference engine, to the NVIDIA L20 (Ada sm_89, 92 SMs, 48 GB): patch set, build tooling, and measured results

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages