环境
- 模型: Huihui-Qwen3.6-35B-A3B-Claude-4.7-Opus-abliterated.i1-Q4_K_M.gguf
- 架构: qwen35moe(Qwen3.5 MoE Hybrid SSM)
- 量化: Q4_K(4.88 BPW),文件 19.70 GiB
- 参数量: 34.66 B(A3B 激活)
- 显卡: 2× Tesla V100-SXM2-16GB(NVLink)
- 软件: llama.cpp build 9263(6a257d446)
层分配(tensor-split = 1,1)
- CUDA0: 层 0-20(共 21 层:16 SSM + 5 full-attn)
- CUDA1: 层 21-40(共 20 层:15 SSM + 5 full-attn)
- output.weight: offloaded to GPU(分配到 CUDA1,因为它分到了最后一层)
- token_embd.weight: CPU(CUDA_Host buffer 不可用),不在 GPU 上
显存分解(nvitop 实测 + 日志)
| 项 | CUDA0 | CUDA1 | 差距 |
|---|---|---|---|
| 模型权重(projected) | 10206 MiB | 9696 MiB | +510 MiB |
| compute buffer | 2073 MiB | 1410 MiB | +663 MiB |
| SSM 循环状态 | 67 MiB | 59 MiB | +8 MiB |
| KV cache(空负载) | 0 MiB | 0 MiB | 0 MiB |
| CUDA 上下文开销 | ~15 MiB | 0 MiB | ~15 MiB |
| 合计(实测 nvitop) | 13924 MiB | 12728 MiB | +1196 MiB |
实际差距 ~1.17 GiB(13924 - 12728),与日志中的 projected 数据完全对得上。
根因分析
1. compute buffer 差距(663 MiB)—— 主因
sched_reserve 分配计算图 scratch buffer 时,CUDA0 作为 pipeline 第一卡需要更大工作空间:
- 处理 embedding 查找转换
- 第一层 attention/SSM 的中间激活
- 跨卡通信的 staging buffer
这是 pipeline parallelism 的固有特性,第一卡永远比后续卡多这些开销。
2. 模型权重差距(510 MiB)—— 次因
CUDA0 多分到 1 层(21 vs 20),每层权重 ~510 MiB @ Q4_K。
注意:之前怀疑是 token_embd.weight(~1GB)占了 0 卡,日志实际显示它走了 CPU(CUDA_Host 不可用),不在 GPU 上。
推论
- embedding 是清白的 —— 不在 GPU,不是差距来源
- output.weight 在 CUDA1 上(~1GB),反而让 CUDA1 多了一些权重——尽管如此 CUDA0 还是更多,说明前两个因素更强
- KV cache 分布均等(各 5 层 full-attn),不是因素
优化建议
方案:偏移 tensor-split 补偿
CUDA0 有 ~663 MiB 的 compute buffer 固有开销,因此需要把更多层倾斜到 CUDA1 来平衡:
# 尝试 0.7,1.3 → 0卡 ~28% 层,1卡 ~72% 层
tensor-split = 0.7,1.3
# 如果仍然不平衡,继续加大 1 卡比例:
tensor-split = 0.5,1.5预期效果:CUDA1 多扛 ~7-8 层 MoE 层后,两卡显存使用趋于相等,单卡能从 ~2220 MiB 空闲释放到各自 ~2800+ MiB。
附录:日志关键行索引
- 层分配:行 312-352(第一次 fitting)/ 1401-1441(第二次实际加载)
- embedding CPU fallback:行 1086
- output layer offload:行 1087
- model buffer 大小:行 1090-1092
- KV cache:行 1148-1150
- SSM RS buffer:行 1193-1194
- compute buffer:行 1215-1217
- memory breakdown:行 1222-1223