构建9254修复了我的TG回归问题,并为NVIDIA GPU添加了PDL支持
摘要
llama.cpp的构建9254修复了一个token生成回归问题,并添加了对NVIDIA GPU的PDL(程序化依赖启动)支持,在新硬件上token生成速度提升高达10%。
在最近的几个构建中,我在mtp和非mtp模型上都遇到了TG回归问题,不得不回退到b9202。但刚运行了新的[b9254](https://github.com/ggml-org/llama.cpp/releases/tag/b9254),TG已恢复,并且在2x5060ti 16gb张量拆分上还额外获得了约5%的性能提升。我用PDL标志编译了cmake来试一下。我打算稍后不启用PDL进行对比测试,但目前在qwen3.6-35b-a3b-Q4_K_XL上得到了一致的结果:3.2k PP和127 tg/s。我并不是说我的结果都是PDL的功劳,但至少这个构建和b9202一样好甚至更好。时间会证明一切。
对话 # [**aendk**](https://github.com/aendk) 评论于[3周前](https://github.com/ggml-org/llama.cpp/pull/22522#issue-4351486947)
# 概述
[程序化依赖启动](https://docs.nvidia.com/cuda/cuda-programming-guide/04-special-topics/programmatic-dependent-launch.html)(PDL)是一项针对较新NVIDIA GPU(CC >= 90;不包括Ada)的CUDA优化技术。它允许同一CUDA流中的CUDA内核重叠执行。与CUDA图类似,它减少了设备上的内核启动开销。两者的优势是相加的(PDL + CG > CG > PDL)。这一点可以通过Nsight Systems截图中单个CUDA流的可视化效果得到最佳体现;本应严格按顺序执行的内核现在并发运行。
PDL早在去年就曾在[#15479](https://github.com/ggml-org/llama.cpp/issues/15479)中提出。这个PR更好地集成了CUDA图语义,并且性能大幅提升。在RTX PRO 6000上,token生成阶段提速10%并不罕见;在DGX Spark上,我观察到了4-5%的改进(模型相关,详见下方详细统计数据)。
要充分发挥PDL的性能,内核需要配备两个新特性:同步屏障(`GGML_CUDA_PDL_SYNC`)和启动信号(`GGML_CUDA_PDL_LC`)。同步屏障限制内核执行,使其等待前一个内核写入的数据,从而避免竞态条件或过早的数据访问。启动信号指示当前内核在哪个点可以容忍下一个内核与其并行启动。此外,内核需要通过新的`ggml_cuda_kernel_launch()`函数启动。同步屏障可以通过仔细检查内核代码,识别内核输入的第一个“真正”数据访问(例如,排除指针算术)来放置。启动信号的放置需要一些手动调整和基准测试。
在这个草案PR中,我启用了`gpt-oss 20b`、`qwen3.5`和`nemotron 120B Super`中使用的所有内核。由于这些内核与其他模型共享,我还测试了更多模型。我几乎在所有模型的token生成阶段都观察到了加速,而预填充/上下文阶段基本上保持中性。
# 应用的启发式规则:
* 在本草案中,对于同步屏障的放置,我假设每个内核的第一个“真正”数据访问是输入张量。如果存在前一个内核输出一个标量,而当前内核在`GGML_CUDA_PDL_SYNC`之前读取该标量的情况,则可能发生数据竞争。在标记为可合并之前,我会再次检查这一点。审查时请牢记。
* `GGML_CUDA_PDL_LC`的正确放置需要一些试错。在一些内核中,我在某些提交中注释掉了一些次优放置,这一点可见一斑。在某些内核中,放置`GGML_CUDA_PDL_LC`甚至会导致性能下降(最明显的是`mul_mat_vec_q`)。通常,信号在核函数中放置得越早,内核的延迟限制就越小,并且内核能承受的共享资源争用(由于后续内核的过早启动)就越多。
# 关于此实现的更多信息
* 即使图中的某些内核未启用PDL,此方法也可用。如果两个连续的内核已启用,它们将利用PDL(例如,许多模型中都存在启用了PDL的`quantize_q8`和`mul_mat_vec_q`)。
* 内核可以逐个启用。
* 优化`GGML_CUDA_PDL_LC`标志的放置需要一些试错,但针对一个模型的良好放置似乎对其他模型也有利。在内部测试中,我没有遇到过例如对模型A有利但对模型B性能更差的情况。
# 已知问题/待办事项
* 目前没有类似memcheck的工具来识别错误放置`GGML_CUDA_PDL_SYNC`情况下的竞态条件。
* 需要找到一种方法自动为不支持的(NVIDIA)GPU禁用PDL。简单地检查`GGML_CUDA_CC_HOPPER`不起作用。
* 更多内核可以迁移到PDL(不同的启动+同步屏障)。
* 需要移除已注释掉的启动信号实验代码。
* 与CUDA图本身一样,最初仅针对token生成推出此功能可能是有意义的。需要检查是否可行。
# 如何测试
你需要拥有较新的NVIDIA GPU(例如Blackwell),并使用`-D GGML_CUDA_PDL=ON`进行编译。
# 如何将其他内核加入PDL
* 步骤1:使用`ggml_cuda_kernel_launch()`修改内核启动,并设置`GGML_CUDA_PDL_SYNC()`。修改内核启动而不设置同步屏障会导致竞态条件。
* 步骤2:迭代放置`GGML_CUDA_PDL_LC()`的位置。我采用的松散启发式方法是将其放在函数开头,测量性能,然后在内核中间的不同位置重复该过程。然后我选择了性能最好的放置位置。在我的测试中,将其放在内核底部附近几乎总是无效的。
相似文章
Blackwell 与 PDL 性能提升
Llama.cpp 现已支持适用于 Blackwell GPU 的 Nvidia 程序化依赖启动 (PDL),在 Token 生成时可带来 5-10% 的性能提升。该功能默认未启用,需通过编译标志开启。
b9200 发布 - 潜在 MTP 提示处理速度提升
llama.cpp 版本 b9200 通过避免不必要的 logits 复制,减少了内存流量,从而提升了多令牌预测(MTP)的提示处理速度。
PSA:如果您几天未更新Llama.cpp,发现MTP性能不佳,请更新Llama.cpp。
更新Llama.cpp可获得显著的token生成速度提升,最高达1.5-1.8倍,并改善提示处理。
MTP+GGML_CUDA_ENABLE_UNIFIED_MEMORY=1 - llama.cpp
一位用户在 llama.cpp 上使用 GGML_CUDA_ENABLE_UNIFIED_MEMORY=1 标志对令牌生成速度进行基准测试,比较启用和未启用 MTP(多令牌预测)时的性能。结果显示,在 RTX5090 上使用 Qwen3.6-27B 模型时,启用 MTP 后速度从 49 tok/s 显著提升至 64 tok/s。
@populartourist: llama.cpp 发布版本 b9235 添加了一些用于提升推理性能的新工具。使用 llama.c 对 RTX 5090 上的 Qwen3.6 27B 进行了基准测试…
llama.cpp 发布版本 b9235 引入了推测性 n-gram 调优,在 RTX 5090 上的 Qwen3.6 27B 上实现了高达约 7 倍的吞吐量提升,其中 k4v96 配置在 10k 和 70k token 测试中表现出最佳的持续性能。