资讯动态

ik_llama.cpp 多 GPU 推理调优:深入解析 GGML_SCHED_MAX_COPIES 与 pipeline parallelism 的 VRAM 权衡

发布时间:2026/9/18 23:34:28 来源:尧图企业网站定制
ik_llama.cpp 多 GPU 推理调优深入解析 GGML_SCHED_MAX_COPIES 与 pipeline parallelism 的 VRAM 权衡【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp导读GGML_SCHED_MAX_COPIES 是 ggml 调度器backend scheduler中控制 pipeline parallelism 输入副本数的编译期宏它直接决定多 GPU 推理时的显存VRAM占用与吞吐表现。本文以 ik_llama.cpp 仓库讨论 #100New argument / env variable for GGML_SCHED_MAX_COPIES?为核心线索从源码层面剖析该宏的启用条件、默认值与影响机制并结合仓库内大量多 GPU 用户的实测记录给出何时该用 1、何时可以调高、如何验证生效的完整实战方案。讨论的起点一个关于能否不重编译就改宏的需求2024-10-21用户 Nexesenex 在 discussion #100 中向项目作者 ikawrakow 提出请求能否提供一个 CLI 参数或至少一个环境变量来设置GGML_SCHED_MAX_COPIES从而避免每次调整都要重新编译。理由是该宏影响 VRAM 占用与性能用户希望在不重编译的前提下方便地做基准测试benching和定制化使用。作者的第一反应是我还没研究过这个它到底有什么用。Nexesenex 随即给出了自己的观察它本意是加速多 GPU 推理主流 llama.cpp 的默认值是 4而他自己设成 1因为当初没有观察到明显提升反而注意到更高的 VRAM 消耗和 GPU 负载。这个讨论虽然没有最终落地成运行时参数但它引出了一个贯穿 ik_llama.cpp 多 GPU 生态的关键实践GGML_SCHED_MAX_COPIES 的值必须在编译期确定而它的选择会显著影响显存分配与推理行为。后文将结合当前仓库源码与大量后续社区记录完整还原这一机制。GGML_SCHED_MAX_COPIES 在源码中的定位编译期宏而非运行时开关GGML_SCHED_MAX_COPIES 是编译期宏通过 CMake 缓存变量注入最终以-D编译定义的形式传给所有 ggml 源文件顶层缓存变量定义于 ggml/CMakeLists.txtset(GGML_SCHED_MAX_COPIES 1 CACHE STRING ggml: max input copies for pipeline parallelism)通过 ggml/src/CMakeLists.txt 写入编译定义add_compile_definitions(GGML_SCHED_MAX_COPIES${GGML_SCHED_MAX_COPIES})在 ggml/src/ggml-backend.cpp 中代码层面还有一层默认值保护#ifndef GGML_SCHED_MAX_COPIES #define GGML_SCHED_MAX_COPIES 1 #endif从源码结构可以推断由于该值参与数组维度声明和图形副本分配它必须是一个编译期常量因此无法在运行时通过环境变量或 CLI 参数直接修改——这正是 discussion #100 中需求的本质障碍。当前仓库中 ik_llama.cpp 的默认值已经是1与讨论发生时主流 llama.cpp 的默认值4不同这一点在后文会详细对比。该宏在调度器中扮演的角色在ggml_backend_sched结构体中pipeline parallelism 支持被明确标注ggml/src/ggml-backend.cpp// pipeline parallelism support int n_copies; int cur_copy; ggml_backend_event_t events[GGML_SCHED_MAX_BACKENDS][GGML_SCHED_MAX_COPIES]; struct ggml_tensor * graph_inputs[GGML_SCHED_MAX_SPLIT_INPUTS]; int n_graph_inputs;关键点在于events数组的第二维直接以GGML_SCHED_MAX_COPIES作为维度大小且张量副本表hv_tensor_copies的分配也与n_copies成正比ggml/src/ggml-backend.cppsched-n_copies parallel ? GGML_SCHED_MAX_COPIES : 1; ... sched-hv_tensor_copies (ggml_tensor **)malloc(sched-hash_set.size * sched-n_backends * sched-n_copies * sizeof(struct ggml_tensor *));也就是说只有启用了 pipeline parallelismparallel true时GGML_SCHED_MAX_COPIES才会生效否则n_copies恒为 1。在图形分割阶段ggml/src/ggml-backend.cpp调度器会为每个后端上的输入张量按n_copies创建多份副本并将这些副本标记为 input/output防止 ggml-alloc 覆盖它们。这些副本会占用额外的设备端GPU与主机端CUDA_Host内存这就是VRAM 占用上升的直接来源。pipeline parallelism 何时被启用llama.cpp 层的条件真正决定parallel取值的逻辑位于 src/llama.cpp// enabling pipeline parallelism in the scheduler increases memory usage, so it is only done when necessary bool pipeline_parallel llama_get_device_count(*model) 1 model-n_gpu_layers (int)model-hparams.n_layer model-split_mode LLAMA_SPLIT_MODE_LAYER params.offload_kqv !model-has_tensor_overrides(); #ifndef GGML_USE_CUDA // pipeline parallelism requires support for async compute and events // currently this is only implemented in the CUDA backend pipeline_parallel false; #endif ctx-sched ggml_backend_sched_new(ctx-backends.data(), backend_buft.data(), ctx-backends.size(), max_nodes, pipeline_parallel); if (pipeline_parallel) { LLAMA_LOG_INFO(%s: pipeline parallelism enabled (n_copies%d)\n, __func__, ggml_backend_sched_get_n_copies(ctx-sched)); }从这段代码可以归纳出 pipeline parallelism 的同时成立条件模型分布在多个设备上llama_get_device_count(*model) 1模型被全量 offload 到 GPUn_gpu_layers n_layer采用按层切分模式LLAMA_SPLIT_MODE_LAYER即默认的-sm layer允许 KV 缓存 offloadparams.offload_kqv即-ov相关参数未使用张量级覆盖!model-has_tensor_overrides()即未使用-ot做精细 offload后端为 CUDA非 CUDA 后端一律关闭。值得注意的注释enabling pipeline parallelism in the scheduler increases memory usage, so it is only done when necessary——即启用该机制本身就会增加内存占用所以仅在上述条件全部满足时才开启。如果启用了 pipeline parallelism启动日志中会出现llama_new_context_with_model: pipeline parallelism enabled (n_copies1)这条日志n_copies的值就是验证编译配置是否生效的最直接依据。一个已知的误启用场景tensor override在 issue #437 中社区成员 saood06 分析了为何很多用户会遇到异常巨大的显存分配pipeline parallelism 的启用条件里使用的是model-n_gpu_layers (int)model-hparams.n_layer这一假设而一旦使用-ottensor override精细指定张量落盘位置这个全量 offload的假设就不再成立但检查逻辑并未感知到这一点从而可能在不该启用时仍然启用。issue #500 中作者 ikawrakow 也确认默认值 4 在某些情况下会导致异常的内存分配insane memory allocations已经有多人遇到同样问题并建议用户先尝试-DGGML_SCHED_MAX_COPIES1。为什么默认值 4 会让多 GPU 用户显存失控讨论 #100 中用户报告mainline 默认 4我设 1 后注意到更多 VRAM 消耗和 GPU 负载后续大量 issue/discussion 给出了具体数字。这里选取仓库内可查证的实例场景n_copies表现issue #500双 30904allocating 360757.13 MiB直接 OOMissue #4374尝试分配1130415.93 MiB约 1.1 TBcudaMalloc failed: out of memorydiscussion #384双 2080Ti 老工作站4尝试分配167771.94 MiBOOM改用 1 后恢复正常约 15 tok/s PP、6 tok/s TGdiscussion #2584→1用户报告rebuilding with-DGGML_SCHED_MAX_COPIES1really reduced VRAM usagePR #23716 卡示例4每张卡 compute buffer 高达5088.02 MiB且各卡完全一致从以上案例可以推断当n_copies 1时调度器为每个后端创建的输入副本、事件对象和计算缓冲区都会成倍增长在多卡 大模型尤其是 DeepSeek/Qwen3 这类大规模 MoE组合下compute buffer 的膨胀可能达到几十 GB 甚至 TB 级最终触发cudaMalloc failed: out of memory。实战如何把 GGML_SCHED_MAX_COPIES 设成 1及验证方法1. 编译时指定当前仓库默认值已经是 1但显式指定可以避免默认值依赖也可在后续升级中保持行为一致cmake -B ./build -DGGML_CUDAON -DGGML_BLASOFF -DGGML_SCHED_MAX_COPIES1 cmake --build ./build --config Release -j $(nproc)该选项也出现在官方 Windows 构建文档 docs/build.md 中可作为标准构建参数之一。仓库内大量多 GPU 用户的推荐组合如 discussion #477、discussion #532、issue #576大致是cmake -B ./build -DCMAKE_BUILD_TYPERelease \ -DGGML_CUDAON \ -DGGML_RPCOFF \ -DGGML_BLASOFF \ -DGGML_SCHED_MAX_COPIES1 \ -DGGML_CUDA_IQK_FORCE_BF161 cmake --build ./build --config Release -j $(nproc)说明-DGGML_CUDA_IQK_FORCE_BF161与GGML_SCHED_MAX_COPIES无直接关系它用于让部分无 MMQ 内核的量化在 CUDA 上使用 bf16 cuBLAS是多 GPU 跑 DeepSeek 等模型时的常见配套项见 ggml/CMakeLists.txt。是否加入取决于你的模型与显卡组合。2. 验证是否生效运行任意可执行文件如llama-server、llama-cli、llama-bench观察启动日志若输出pipeline parallelism enabled (n_copies1)说明以 1 份副本运行若输出pipeline parallelism enabled (n_copies4)说明构建时该宏仍为 4或该宏未被覆盖若不出现该日志说明当前运行配置未满足启用条件例如单 GPU、未全量 offload、使用-ot、或非 CUDA 后端此时该宏不生效。同时对照 compute buffer 数值例如 PR #492 中 n_copies1 时的典型输出llama_new_context_with_model: pipeline parallelism enabled (n_copies1) llama_new_context_with_model: CUDA0 compute buffer size 2094.00 MiB llama_new_context_with_model: CUDA1 compute buffer size 2125.00 MiB llama_new_context_with_model: CUDA_Host compute buffer size 932.00 MiB3. 与相关运行参数的配合从 discussion #258 中社区总结的经验来看编译使用-DGGML_SCHED_MAX_COPIES1后多 GPU 显存分配更符合预期释放出的显存可以用于增大--batch-size/--ubatch-size例如-b 4096 -ub 4096通过更大批量换取更高的 prefillPP吞吐见 issue #425 中作者的回复若仍需要更细粒度的显存控制可以配合-ottensor override把部分层或专家张量留在 CPU但要意识到-ot会破坏 pipeline parallelism 的启用假设见 issue #437 的分析。权衡n_copies1 与更高值的取舍为什么有人愿意用更高的 n_copies讨论 #100 中提到它本意是加速多 GPU 推理。从实现看pipeline parallelism 的目标是让不同后端多张 GPU的计算与拷贝尽量重叠调度器为输入张量维护多份副本并借助后端事件events做流水线同步ggml/src/ggml-backend.cpp这在理想情况下能提升吞吐。这也是主流 llama.cpp 长期默认4的原因。为什么多数 ik_llama.cpp 多 GPU 用户选择 1社区在 discussion #459 中的共识是默认 4 会让多 GPU 用户分配出远超预期的显存设为 1 后显存占用符合预期、更简单直观然后通过加大 batch 来提速。另有用户指出由于大多数人已经用-ot手动决定各张量落在哪张卡上pipeline 副本带来的收益变得可有可无Exact same results as taking a single layer off。综合仓库证据可以给出如下决策建议多 GPU 跑大模型、且显存紧张优先1这是当前仓库默认值也是绝大多数实战记录的选择追求极致吞吐、显存充裕、且未使用-ot可以尝试更高值如 4并对照pipeline parallelism enabled (n_copies4)日志与 compute buffer 大小做基准对比观察 PP/TG 的实际变化任何显存异常暴涨 / 莫名 OOM先确认构建时该宏是否为 1再排查是否误触发了 pipeline parallelism 的启用条件。总结回到 discussion #100 的原始诉求GGML_SCHED_MAX_COPIES 本质是编译期宏受限于数组维度和副本分配机制无法在运行时用 CLI 参数或环境变量调整因此在当前 ik_llama.cpp 中改它必须重编译。但仓库演进给出了更优解ik_llama.cpp 已将默认值从主流的 4 改为 1ggml/CMakeLists.txt从源头规避了多 GPU 用户最常见的显存失控问题。对开发者而言本主题的可操作结论是构建时显式传入-DGGML_SCHED_MAX_COPIES1保证多 GPU 显存分配可控用启动日志中的pipeline parallelism enabled (n_copies...)验证编译配置与运行路径理解 pipeline parallelism 的启用条件多设备、全量 offload、layer split、offload_kqv、无 tensor override、CUDA 后端避免在错误场景下依赖或误解该宏在显存允许的前提下用更大的 batch/ubatch 换取 PP 吞吐而不是盲目调高 n_copies。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价