AI Pulse

不对称V100组合,27B模型预填充1,376 tok/s

核心结论

我已经把一块16GB和一块32GB的不对称Tesla V100-PCIE组合起来(共48GB),在Proxmox/LXC环境中搭了一个本地推理实验室。目前最实用的配置是:使用最新的CUDA版llama.cpp,加上tensor split、Flash Attention、--numa distribute和大batch。在Qwen3.8 27B Q6_K_M模型上,我测得的性能为:2k上下文时预填充1,376.9 tok/s,4k时1,324.3 tok/s,16k时1,221.5 tok/s,解码39.9 tok/s。我还用Qwen3.8 Flash Next Q4 GGUF(约177B总参数、6B激活的MoE)在同样的两张卡上跑通了,解码约26 tok/s,预填充380 PP。

这里要说明一下:这并不意味着老V100能胜过现代GPU。只是在这些“一颗肾换一块GPU”的疯狂市场行情下,记录一下它们能做到什么。

硬件配置

项目配置
GPU 0NVIDIA Tesla V100-PCIE,16,144 MiB VRAM,compute capability 7.0
GPU 1NVIDIA Tesla V100-PCIE,32,494 MiB VRAM,compute capability 7.0
服务器拓扑NUMA-aware 主机;基准测试使用 numactl --interleave=all--numa distribute
虚拟化Proxmox 配合 LXC 推理容器,隔离运行时
CPU2 x Xeon E5-2696 v4 @ 2.20GHz
RAM512 GB DDR4 2400 ECC RDIMM
CUDA 栈CUDA 12.8;NVIDIA 驱动库按需挂载到 vLLM LXC 容器内

基准测试结果

负载运行时/模型最佳结果背后的配置
最佳 27B 预填充mainline llama.cpp,Qwen3.8 27B Q6_K_M1,376.87 pp tok/s(2k)Tensor split;主 GPU = 32GB V100;Flash Attention;Q8 KV;batch/ubatch 2048;20线程;NUMA distribute
最佳 27B 解码mainline llama.cpp,Qwen3.8 27B Q6_K_M39.88 tg tok/s同上
27B @ 16k 上下文mainline llama.cpp,Qwen3.8 27B Q6_K_M1,221.49 pp tok/s同上
最佳 Q8 27B 预填充mainline llama.cpp,Qwen3.8 27B Q8_K_XL1,059.67 pp tok/s(2k)Tensor split;长上下文运行使用 1:1.5 切分
Q8 27B @ 64kmainline llama.cpp,Qwen3.8 27B Q8_K_XL664.36 pp tok/s65,536 token 的 prompt;tensor split 1:1.5
最佳 ik_llama 27Bik_llama.cpp,Qwen3.8 27B Q6_K_M670.61 pp tok/s(2k);35.43 tg tok/sGraph split;1:1 切分;batch 更小
测试过最大的模型ik_llama.cpp,Qwen3.8 Flash Next Q4_K_XL380.67 pp tok/s(4k);26.29 tg tok/s103.68 GiB GGUF;176.94B 总参数 / 6B 激活 MoE;graph split 1:1.75
ninfer-v100NInfer,Qwen3.8 27B NVFP4927.54 pp tok/s(8,190)19.7 GiB 权重;INT8 KV;MTP

调优启示

Qwen3.8 27B Q6_K_M,mainline llama.cpppp2048tg128
Layer split,79线程981.7922.88
Tensor split,20线程1,371.5738.79
Tensor split,主GPU = 32GB V1001,376.8739.88
Tensor split 1:2980.7126.10
不切分967.3618.05

在这个不对称V100组合上,决定性的因素是 tensor split(而非 layer split),以及把 32GB V100 设为主 GPU。最适合日常使用的路径是:mainline llama.cpp + Qwen3.8 27B Q6 + tensor split + Flash Attention + Q8 KV + NUMA distribution。

实测命令

下面的命令是我用来复现 27B Q6 指标的 llama.cpp 基准测试。关键参数包括 -sm tensor(tensor split)、-mg 1(把主 GPU 设为设备 1,即 32GB V100)、-fa on(Flash Attention)、-b 2048-ub 2048(batch / ubatch)、-t 20(线程数)、--numa distribute

root@llama-cpp:~# numactl --interleave=all /opt/llama.cpp/build/bin/llama-bench -m /path/to/Qwen3.8-27B-UD-Q6_K_M.gguf -ngl 999 -sm tensor -mg 1 -fa on -b 2048 -ub 2048 -t 20 -p 512,2048,4096,16384 -n 128 -r 5 -lm none --numa distribute

输出片段:

ggml_cuda_init: found 2 CUDA devices (Total VRAM: 48638 MiB):
  Device 0: Tesla V100-PCIE-16GB, compute capability 7.0, VMM: yes, VRAM: 16144 MiB
  Device 1: Tesla V100-PCIE-32GB, compute capability 7.0, VMM: yes, VRAM: 32494 MiB

| model                         |       size |     params | backend    | ngl | threads | n_ubatch |   main_gpu |     sm |  fa |         lm |            test |                  t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | ------: | -------: | ---------: | -----: | --: | ---------: | --------------: | -------------------: |
| qwen35 27B Q6_K                |  21.49 GiB |    27.32 B | CUDA       | 999 |      20 |     2048 |          1 | tensor |   1 |       none |           pp512 |      1156.94 ± 5.10 |
| qwen35 27B Q6_K                |  21.49 GiB |    27.32 B | CUDA       | 999 |      20 |     2048 |          1 | tensor |   1 |       none |          pp2048 |      1376.87 ± 4.59 |
| qwen35 27B Q6_K                |  21.49 GiB |    27.32 B | CUDA       | 999 |      20 |     2048 |          1 | tensor |   1 |       none |          pp4096 |      1324.34 ± 7.37 |
| qwen35 27B Q6_K                |  21.49 GiB |    27.32 B | CUDA       | 999 |      20 |     2048 |          1 | tensor |   1 |       none |         pp16384 |      1221.49 ± 3.87 |
| qwen35 27B Q6_K                |  21.49 GiB |    27.32 B | CUDA       | 999 |      20 |     2048 |          1 | tensor |   1 |       none |           tg128 |        39.88 ± 0.04 |

build: b29c606 (1)

要在同一对 V100 上跑 Qwen3.8 Flash Next Q4_K_XL(103.68 GiB GGUF),我用了 ik_llama 分支。下面的循环分别把主 GPU 设为 0 和 1 对比结果;-sm graph -ts 1/1.75 指定图切分及 1:1.75 的不均匀比例。

root@llama-cpp:~# for MAIN in 0 1; do
  echo
  echo "===== MAIN GPU ${MAIN} ====="
  /usr/bin/numactl --interleave=all \\
/opt/ik_llama.cpp/build/bin/llama-bench \\
\-m /path/to/models/Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf -ngl 999 -sm graph -ts 1/1.75 -mg "$MAIN" --fit 1 --fit-margin 1024 -fa 1 -p 4096,8192 -n 128 -b 2048 -ub 1024 -r 10 -o md
done 2>&1 | tee /root/bench-qwen38-main-gpu-final.md

输出中可以看到两个主 GPU 设置下的结果:

===== MAIN GPU 0 =====
ggml_cuda_init: found 2 CUDA devices:
  Device 0: Tesla V100-PCIE-16GB, compute capability 7.0, VMM: yes, VRAM: 16144 MiB
  Device 1: Tesla V100-PCIE-32GB, compute capability 7.0, VMM: yes, VRAM: 32494 MiB

| model                         |       size |     params | backend    | ngl | n_ubatch |    sm | ts           |          test |              t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | -------: | ----: | ------------ | ------------: | ---------------: |
| qwen4exp 125B.A6B Q4_K - Medium | 103.68 GiB |  176.94 B | CUDA       | 999 |    1024 | graph | 1.00/1.75    |        pp4096 |   356.20 ± 7.68 |
| qwen4exp 125B.A6B Q4_K - Medium | 103.68 GiB |  176.94 B | CUDA       | 999 |    1024 | graph | 1.00/1.75    |        pp8192 |   361.00 ± 4.72 |
| qwen4exp 125B.A6B Q4_K - Medium | 103.68 GiB |  176.94 B | CUDA       | 999 |    1024 | graph | 1.00/1.75    |         tg128 |    26.44 ± 0.11 |

===== MAIN GPU 1 =====
ggml_cuda_init: found 2 CUDA devices:
  Device 0: Tesla V100-PCIE-16GB, compute capability 7.0, VMM: yes, VRAM: 16144 MiB
  Device 1: Tesla V100-PCIE-32GB, compute capability 7.0, VMM: yes, VRAM: 32494 MiB

| model                         |       size |     params | backend    | ngl | n_ubatch |   main_gpu |    sm | ts           |          test |              t/s |
| ------------------------------ | ---------: | ---------: | ---------- | --: | -------: | ---------: | ----: | ------------ | ------------: | ---------------: |
| qwen4exp 125B.A6B Q4_K - Medium | 103.68 GiB |  176.94 B | CUDA       | 999 |    1024 |          1 | graph | 1.00/1.75    |        pp4096 |   380.67 ± 6.25 |
| qwen4exp 125B.A6B Q4_K - Medium | 103.68 GiB |  176.94 B | CUDA       | 999 |    1024 |          1 | graph | 1.00/1.75    |        pp8192 |   360.75 ± 5.05 |
| qwen4exp 125B.A6B Q4_K - Medium | 103.68 GiB |  176.94 B | CUDA       | 999 |    1024 |          1 | graph | 1.00/1.75    |         tg128 |    26.29 ± 0.08 |

build: 1a2a8604 (4878)

作者附言

我很想了解更多关于这些 flags 的细节,因为我在这个方面还是新手……感谢你的关注。

社区讨论

—— 高票评论(原帖共 13 条讨论)——

u/FullstackSensei:移除表格格式中的虚线……才能让表格正常显示。

u/BP041:--numa distribute 标志在多 GPU 设置中才是真正的技巧。对于不对称配对,我试过按显存比例切分层而不是原始 tensor split,这样能避免带宽瓶颈。你在 16GB 和 32GB 之间的切分比例是多少?

u/coronafire:感谢分享。那些预填充率看起来特别棒,比我的好多了,不过这可能也跟我的硬件有关。我有两张 V100 16GB,但是插在同一张 PCI 卡上,所以每条总线只有 x8。也有可能是我那个临时拼凑的基准脚本不够严谨。我好玩地把两个 NInfer fork 缝在一起,实现了双 V100 支持,比 llama.cpp 的性能提升明显。你可以在这里试用:https://github.com/andrewleech/ninfer-v100/blob/v100_dual/docs/PREFILL-BENCHMARK.md 我的设置里也有不对称切分,以便在本来配对的两张卡上为 MTP 或视觉模型腾出空间。

u/Traditional_Bell8153:我单张 V100 也有同样的 pp 和 tg 数值。

u/Sitkin_Marrel:从一行到下一行,tg/s 从 22.88 跳到 38.79,预填充也提升了将近 400。即使同时改两个变量,我也会逐字照抄那个配置。

u/Cloudsurfer_90:不错。不对称配对正是手动 tensor split 值得用的地方,因为 llama.cpp 默认会均匀切分,那样 32GB 卡会有一半空闲,而 16GB 卡成为瓶颈。把切分权重偏向大卡而不是 50/50,通常就是白拿性能的地方。另外,在没有 NVLink 的 PCIe V100 上,每个 token 的跨卡流量是真正的天花板,所以让每张卡保留连续的 layer 范围而不是交织排列很重要;KV cache 落在哪里和权重同样重要。一个被 offload 的 cache 如果不断在 PCIe 链路上颠簸,就会吃掉你发布的 decode tok/s。我好奇你最终用的切分比例是多少,以及是否把 KV cache 固定到了某一张卡上。在两块 V100 上跑 27B Q6 做到 40 decode,对这个硬件来说是很扎实的结果。

阅读原文
📚 相关主题 大语言模型本地部署llama.cpp

订阅 AI Pulse

每天 08:00 · 12:30 · 18:30 · 23:50 更新