llama.cpp 分布式推理实战:ggml-rpc 远程设备共享与 RPC 后端详解
【免费下载链接】llama.cppLLM inference in C/C++项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp
llama.cpp 通过ggml-rpc-server可以把远程主机上的 GPU、CPU 等加速设备暴露给其他机器,再由 RPC 后端(RPC backend)把计算任务 offload 过去,从而在多台异构主机之间完成分布式 LLM 推理。本文基于仓库中的 RPC 文档,结合 rpc-server.cpp、ggml-rpc.cpp 等源码,讲清整套方案的架构、构建部署步骤、关键参数与传输层优化,帮助你在局域网内安全地搭起一套跨主机的 llama.cpp 推理环境。
重要安全提示:该示例与 RPC 后端目前仍处于概念验证(proof-of-concept)阶段,功能尚不稳定且不保证安全性。切勿将 RPC server 暴露在开放网络或敏感环境中运行,只应在受信任的局域网内使用。
整体架构:一台主控机,多个远程设备池
RPC 方案的角色划分非常清晰:每台远程主机运行一个ggml-rpc-server,把本机的加速设备(CUDA、Metal、CPU 等)通过 TCP 暴露出去;主控机(Main Host)上的llama-cli/llama-server内建 RPC 后端,可同时连接多个 RPC server,把算子和张量计算分发到各端执行。原始文档中给出的架构示意如下:
从图中可以看到两种典型拓扑:
- 实线连接:主 RPC 通道,负责模型权重、KV cache 与算子计算的分发;
- 虚线连接:从源码结构看,这代表可弹性加入/降级的备用主机(Host N),其上的 CUDA 与 CPU 设备均可被纳入计算池。
ggml-rpc-server默认暴露本机所有可用的加速设备;如果主机上没有任何加速器,则暴露单个CPU设备。
远程主机:构建并启动 ggml-rpc-server
构建:为各加速器编译后端
在每台远程主机上,需要在常规构建选项之外追加-DGGML_RPC=ON,把相应加速器后端一并编进ggml-rpc-server。以 CUDA 为例:
mkdir build-rpc-cuda cd build-rpc-cuda cmake .. -DGGML_CUDA=ON -DGGML_RPC=ON cmake --build . --config Release构建产物ggml-rpc-server由 tools/rpc/CMakeLists.txt 定义:它编译 rpc-server.cpp 并链接ggml库。当LLAMA_BUILD_TESTS=ON且非动态后端加载模式时,该 CMake 文件还会一并生成多服务器集成测试test-rpc-multi-server(对应 tests/test-rpc-multi-server.cpp 与 tests/test-rpc-multi-server.sh),用于验证客户端与多个ggml-rpc-server实例的协同工作能力。
启动与设备检测输出
启动后,server 会自动检测并暴露本机设备,典型输出如下(取自原始文档的示例运行记录):
$ bin/ggml-rpc-server ggml_cuda_init: GGML_CUDA_FORCE_MMQ: no ggml_cuda_init: GGML_CUDA_FORCE_CUBLAS: no ggml_cuda_init: found 1 CUDA devices: Device 0: NVIDIA GeForce RTX 5090, compute capability 12.0, VMM: yes Starting RPC server v3.0.0 endpoint : 127.0.0.1:50052 local cache : n/a Devices: CUDA0: NVIDIA GeForce RTX 5090 (32109 MiB, 31588 MiB free)其中endpoint显示监听地址与端口,Devices段列出被暴露设备的型号与显存容量/可用量——这两项信息是主控机侧做显存比例分配的依据。
完整命令行参数
rpc-server.cpp 中的print_usage输出了 server 的全部参数,默认值定义在 rpc_server_params 结构中:
| 参数 | 说明 | 默认值 |
|---|---|---|
-h, --help | 显示帮助信息 | — |
-t, --threads N | CPU 设备使用的线程数 | hardware_concurrency()/2(至少 1) |
-d, --device <dev1,dev2,...> | 逗号分隔的待暴露设备列表 | 全部可用设备 |
-H, --host HOST | 绑定地址 | 127.0.0.1 |
-p, --port PORT | 监听端口(1–65535) | 50052 |
-c, --cache | 启用本地文件缓存 | 关闭 |
注意默认 host 是127.0.0.1:跨机使用时务必通过-H 0.0.0.0(或具体网卡地址)让其他主机能够访问。--device参数在解析时以逗号或斜杠为分隔符(见 参数解析逻辑),因此CUDA0这类带编号的设备名不受影响。
控制暴露哪些 CUDA 设备
可以通过CUDA_VISIBLE_DEVICES环境变量或--device命令行选项限定暴露的设备集合。以下两条命令效果等价:
$ CUDA_VISIBLE_DEVICES=0 bin/ggml-rpc-server -p 50052 $ bin/ggml-rpc-server --device CUDA0 -p 50052主控机:使用 --rpc 接入远程设备
构建与连接
在主控机上,正常编译本机所需的后端,并额外加上-DGGML_RPC=ON。随后运行llama-cli或llama-server时,用--rpc选项指定各ggml-rpc-server的host:port(多个用逗号分隔):
$ llama-cli -hf ggml-org/gemma-3-1b-it-GGUF -ngl 99 --rpc 192.168.88.10:50052,192.168.88.11:50052--rpc在 common/arg.cpp 中注册,仅当编译产物llama_supports_rpc()为真时才会出现;它同样支持通过环境变量LLAMA_ARG_RPC传入服务器列表。其回调函数 add_rpc_devices 会按逗号拆分服务器列表,通过ggml_backend_reg_by_name("RPC")找到 RPC 后端注册表,再逐一调用ggml_backend_rpc_add_server把每个 endpoint 注册为一个可参与推理的远程设备。源码中有一条值得留意的注释:-mmdev(多模态设备选项)在参数映射表中必须排在--rpc之后处理,否则 RPC 设备尚未注册完成——这也解释了为何 RPC 设备是作为普通ggml设备被整个 llama.cpp 设备管理体系统一调度的。
权重与 KV cache 的跨设备分布
默认情况下,llama.cpp 会把模型权重和 KV cache按各设备(含本地与远程)可用显存的比例分摊到所有可用设备上。如果想自定义分配比例,可以使用--tensor-split选项显式指定每个设备分得的份额。
本地缓存:避免大张量反复过网络
RPC server 支持把大张量写入本地文件缓存,避免同一份数据反复通过网络传输,对大模型加载速度的改善尤为明显。启用方式:
$ bin/ggml-rpc-server -c关于缓存目录的落盘规则,rpc-server.cpp 中有一段从 common.cpp 拷贝过来的fs_get_cache_directory()(为免链接 libcommon 而复制)。其优先级为:
- 设置了
LLAMA_CACHE环境变量时,直接使用其指向的目录; - 否则在 Linux/BSD 上依次回退到
XDG_CACHE_HOME、$HOME/.cache/(甚至getpwuid兜底),macOS 用~/Library/Caches/,Windows 用LOCALAPPDATA; - 最终在该目录下追加
llama.cpp/子目录。
文档中给出的默认路径$HOME/.cache/llama.cpp/rpc即来源于此(Linux 平台下~/.cache/llama.cpp/rpc)。
从实现层面看,缓存策略与 RPC 协议中的RPC_CMD_SET_TENSOR_HASH命令配合:当张量数据大小超过约 10 MiB(HASH_THRESHOLD)时,客户端会先按哈希检查服务端/缓存中是否已有相同数据,命中则跳过整块传输,这正是“大张量本地缓存”收益的来源。
传输层:TCP 之上的 RDMA 可选加速
RPC 后端除 TCP 外还支持 RDMA 传输,以获得更低延迟与更高吞吐。关键设计是传输方式在初始握手阶段协商——命令行用法完全不变,只有当通信双方都具备 RDMA 能力时才切换,否则自动回退到 TCP。
支持两个 RDMA 提供方,且在构建时找到对应库即默认启用:
- Linux:通过
libibverbs使用支持 RoCEv2 的网卡(例如 Mellanox ConnectX); - macOS:通过
librdma使用 Apple 芯片 Mac 上的 Thunderbolt 5 RDMA,要求 macOS 26.2 或更新版本,且需要先在 macOS Recovery 中执行rdma_ctl enable启用 RDMA 一次。
由于 RDMA 是点对点连接,每一侧都会选用与连接地址 GID 匹配的本地设备。要真正走 RDMA,必须让--rpc指向 RDMA 链路可达的地址——在 Thunderbolt 场景下,--rpc中应使用对端 Mac 的 Thunderbolt 地址;如果连接是从其他接口建立的,该连接仍会停留在 TCP 上。
如果希望不重新编译就强制禁用 RDMA、走纯 TCP,在任一侧设置环境变量即可:
$ GGML_RPC_NO_RDMA=1 bin/ggml-rpc-server这一点可以在 transport.cpp 中得到印证:传输层初始化时检查GGML_RPC_NO_RDMA,一旦设置即放弃 RDMA 探测。另外,transport.h 定义了单块最大 1 GiB 的MAX_CHUNK_SIZE,并说明flush()必须在每条消息边界调用——RDMA 传输会把多次写操作合并成定长帧,只有 flush 时才会把尾部不完整帧发出;TCP 上该调用为空操作。这正是两种传输可以共用同一套消息接口的关键。
协议与调试:从源码看 RPC 如何工作
命令集
ggml-rpc.cpp 定义了双方通信的全部 RPC 命令(rpc_cmd枚举),涵盖了远程计算所需的生命周期:
- 设备与内存:
RPC_CMD_DEVICE_COUNT(设备数量)、RPC_CMD_GET_DEVICE_MEMORY(设备显存)、RPC_CMD_ALLOC_BUFFER/RPC_CMD_FREE_BUFFER/RPC_CMD_BUFFER_CLEAR、RPC_CMD_GET_ALIGNMENT/RPC_CMD_GET_MAX_SIZE; - 张量传输:
RPC_CMD_SET_TENSOR、RPC_CMD_SET_TENSOR_HASH(哈希去重)、RPC_CMD_GET_TENSOR、RPC_CMD_COPY_TENSOR、RPC_CMD_INIT_TENSOR、RPC_CMD_MEMSET_TENSOR; - 图计算:
RPC_CMD_GRAPH_COMPUTE、RPC_CMD_GRAPH_RECOMPUTE(重算)、RPC_CMD_GET_ALLOC_SIZE(预估计算分配量)。
所有消息结构均使用#pragma pack(push, 1)紧凑打包(如 rpc_tensor 完整序列化ggml_tensor的维度、步长、算子与源张量引用),以减少网络传输体积。协议版本兼容通过RPC_CMD_HELLO握手消息中的 major/minor/patch 版本与能力位(conn_caps)协商,这也是 RDMA 能力能够“握手期自动探测”的基础。
故障排查
遇到连接、设备暴露或计算异常时,可开启 server 侧调试日志:
$ GGML_RPC_DEBUG=1 bin/ggml-rpc-serverggml-rpc.cpp 中,GGML_RPC_DEBUG环境变量一旦被设置,LOG_DBG宏就会把GGML_LOG_DEBUG级别的详细消息输出到控制台,便于定位是哪一步 RPC 交互出了问题。此外,客户端在收到异常响应时会以 “Remote RPC server crashed or returned malformed response” 中止(RPC_STATUS_ASSERT),看到这个报错基本可以判定是对端 server 崩溃或响应格式损坏,应先在远程主机上用GGML_RPC_DEBUG=1复现。
小结:适用边界与最佳实践
- 拓扑:一台主控机 + N 台运行
ggml-rpc-server的远程主机;默认按显存比例自动分摊权重与 KV cache,需要精细控制时用--tensor-split覆盖。 - 部署清单:远程机
cmake .. -DGGML_CUDA=ON -DGGML_RPC=ON构建 → 以-H 0.0.0.0 -p 50052启动(注意默认只绑定 127.0.0.1)→ 主控机--rpc host1:port,host2:port接入。 - 性能手段:大模型加
-c启用本地缓存(目录受LLAMA_CACHE控制);高速网络环境可利用握手自动协商的 RDMA,必要时用GGML_RPC_NO_RDMA=1排除干扰。 - 边界提醒:RPC 后端仍处于概念验证阶段,接口与行为可能随版本变动,且不提供网络安全保证——仅限受信任网络内部实验使用,不要部署到公网或敏感环境。
<输出文章>
(注:以上正文即为最终文章,标签已闭合。)
抱歉,上文末尾多出了一行说明文字,以下为符合格式的最终输出:
<输出文章>
llama.cpp 分布式推理实战:ggml-rpc 远程设备共享与 RPC 后端详解
llama.cpp 通过ggml-rpc-server可以把远程主机上的 GPU、CPU 等加速设备暴露给其他机器,再由 RPC 后端(RPC backend)把计算任务 offload 过去,从而在多台异构主机之间完成分布式 LLM 推理。本文基于仓库中的 RPC 文档,结合 rpc-server.cpp、ggml-rpc.cpp 等源码,讲清整套方案的架构、构建部署步骤、关键参数与传输层优化,帮助你在局域网内安全地搭起一套跨主机的 llama.cpp 推理环境。
重要安全提示:该示例与 RPC 后端目前仍处于概念验证(proof-of-concept)阶段,功能尚不稳定且不保证安全性。切勿将 RPC server 暴露在开放网络或敏感环境中运行,只应在受信任的局域网内使用。
整体架构:一台主控机,多个远程设备池
RPC 方案的角色划分非常清晰:每台远程主机运行一个ggml-rpc-server,把本机的加速设备(CUDA、Metal、CPU 等)通过 TCP 暴露出去;主控机(Main Host)上的llama-cli/llama-server内建 RPC 后端,可同时连接多个 RPC server,把算子和张量计算分发到各端执行。原始文档中给出的架构示意如下:
从图中可以看到两种典型连接形态:
- 实线连接:主 RPC 通道,负责模型权重、KV cache 与算子计算的分发;
- 虚线连接:从源码结构看,这代表可弹性加入的备用主机(Host N),其上的 CUDA 与 CPU 设备均可被纳入计算池。
ggml-rpc-server默认暴露本机所有可用的加速设备;如果主机上没有任何加速器,则暴露单个CPU设备。
远程主机:构建并启动 ggml-rpc-server
构建:为各加速器编译后端
在每台远程主机上,需要在常规构建选项之外追加-DGGML_RPC=ON,把相应加速器后端一并编进ggml-rpc-server。以 CUDA 为例:
mkdir build-rpc-cuda cd build-rpc-cuda cmake .. -DGGML_CUDA=ON -DGGML_RPC=ON cmake --build . --config Release构建目标ggml-rpc-server由 tools/rpc/CMakeLists.txt 定义:它编译 rpc-server.cpp 并链接ggml库。当LLAMA_BUILD_TESTS=ON且非动态后端加载模式(UNIX 平台)时,该 CMake 文件还会一并生成多服务器集成测试test-rpc-multi-server(对应 tests/test-rpc-multi-server.cpp 与 tests/test-rpc-multi-server.sh),用于验证客户端与多个ggml-rpc-server实例的协同工作能力。
启动与设备检测输出
启动后,server 会自动检测并暴露本机设备,典型输出如下(取自原始文档的示例运行记录):
$ bin/ggml-rpc-server ggml_cuda_init: GGML_CUDA_FORCE_MMQ: no ggml_cuda_init: GGML_CUDA_FORCE_CUBLAS: no ggml_cuda_init: found 1 CUDA devices: Device 0: NVIDIA GeForce RTX 5090, compute capability 12.0, VMM: yes Starting RPC server v3.0.0 endpoint : 127.0.0.1:50052 local cache : n/a Devices: CUDA0: NVIDIA GeForce RTX 5090 (32109 MiB, 31588 MiB free)其中endpoint显示监听地址与端口,local cache指示缓存是否启用,Devices段列出被暴露设备的型号与显存总量/可用量——这两项信息是主控机侧做显存比例分配的依据。
完整命令行参数
rpc-server.cpp 中的print_usage输出了 server 的全部参数,默认值定义在 rpc_server_params 结构中:
| 参数 | 说明 | 默认值 |
|---|---|---|
-h, --help | 显示帮助信息 | — |
-t, --threads N | CPU 设备使用的线程数 | hardware_concurrency()/2(至少 1) |
-d, --device <dev1,dev2,...> | 逗号分隔的待暴露设备列表 | 全部可用设备 |
-H, --host HOST | 绑定地址 | 127.0.0.1 |
-p, --port PORT | 监听端口(1–65535) | 50052 |
-c, --cache | 启用本地文件缓存 | 关闭 |
注意默认 host 是127.0.0.1:跨机使用时务必通过-H 0.0.0.0(或具体网卡地址)让其他主机能够访问。--device参数在解析时以逗号或斜杠为分隔符(见 参数解析逻辑),因此CUDA0这类带编号的设备名不受影响。
控制暴露哪些 CUDA 设备
可以通过CUDA_VISIBLE_DEVICES环境变量或--device命令行选项限定暴露的设备集合。以下两条命令效果等价:
$ CUDA_VISIBLE_DEVICES=0 bin/ggml-rpc-server -p 50052 $ bin/ggml-rpc-server --device CUDA0 -p 50052主控机:使用 --rpc 接入远程设备
构建与连接
在主控机上,正常编译本机所需的后端,并额外加上-DGGML_RPC=ON。随后运行llama-cli或llama-server时,用--rpc选项指定各ggml-rpc-server的host:port(多个用逗号分隔):
$ llama-cli -hf ggml-org/gemma-3-1b-it-GGUF -ngl 99 --rpc 192.168.88.10:50052,192.168.88.11:50052--rpc在 common/arg.cpp 中注册,仅当编译产物llama_supports_rpc()为真时才会出现;它同样支持通过环境变量LLAMA_ARG_RPC传入服务器列表。其回调函数 add_rpc_devices 会按逗号拆分服务器列表,通过ggml_backend_reg_by_name("RPC")找到 RPC 后端注册表,再逐一调用ggml_backend_rpc_add_server把每个 endpoint 注册为一个可参与推理的远程设备。源码中有一条值得留意的注释:-mmdev(多模态设备选项)在参数映射表中必须排在--rpc之后处理,否则 RPC 设备尚未注册完成——这也解释了 RPC 设备是作为普通ggml设备被整个 llama.cpp 设备管理体系统一调度的。
权重与 KV cache 的跨设备分布
默认情况下,llama.cpp 会把模型权重和 KV cache按各设备(含本地与远程)可用显存的比例分摊到所有可用设备上。如果想自定义分配比例,可以使用--tensor-split选项显式指定每个设备分得的份额。
本地缓存:避免大张量反复过网络
RPC server 支持把大张量写入本地文件缓存,避免同一份数据反复通过网络传输,对大模型加载速度的改善尤为明显。启用方式:
$ bin/ggml-rpc-server -c关于缓存目录的落盘规则,rpc-server.cpp 中有一段从 common.cpp 拷贝过来的fs_get_cache_directory()(为免链接 libcommon 而复制)。其优先级为:
- 设置了
LLAMA_CACHE环境变量时,直接使用其指向的目录; - 否则在 Linux/BSD 上依次回退到
XDG_CACHE_HOME、$HOME/.cache/(乃至getpwuid兜底),macOS 用~/Library/Caches/,Windows 用LOCALAPPDATA; - 最终在该目录下追加
llama.cpp/子目录。
文档中给出的默认路径$HOME/.cache/llama.cpp/rpc即来源于此(Linux 平台下为~/.cache/llama.cpp/rpc)。
从实现层面看,缓存策略与 RPC 协议中的RPC_CMD_SET_TENSOR_HASH命令配合:当张量数据大小超过约 10 MiB(HASH_THRESHOLD)时,客户端会优先走哈希校验路径,命中已有数据则跳过整块传输,这正是“大张量本地缓存”收益的来源。
传输层:TCP 之上的 RDMA 可选加速
RPC 后端除 TCP 外还支持 RDMA 传输,以获得更低延迟与更高吞吐。关键设计是传输方式在初始握手阶段协商——命令行用法完全不变,只有当通信双方都具备 RDMA 能力时才切换,否则自动回退到 TCP。
支持两个 RDMA 提供方,且在构建时找到对应库即默认启用:
- Linux:通过
libibverbs使用支持 RoCEv2 的网卡(例如 Mellanox ConnectX); - macOS:通过
librdma使用 Apple 芯片 Mac 上的 Thunderbolt 5 RDMA,要求 macOS 26.2 或更新版本,且需要先在 macOS Recovery 中执行rdma_ctl enable启用 RDMA 一次。
由于 RDMA 是点对点连接,每一侧都会选用与连接地址 GID 匹配的本地设备。要真正走 RDMA,必须让--rpc指向 RDMA 链路可达的地址——在 Thunderbolt 场景下,--rpc中应使用对端 Mac 的 Thunderbolt 地址;如果连接是从其他接口建立的,该连接仍会停留在 TCP 上。
如果希望不重新编译就强制禁用 RDMA、走纯 TCP,在任一侧设置环境变量即可:
$ GGML_RPC_NO_RDMA=1 bin/ggml-rpc-server这一点可以在 transport.cpp 中得到印证:传输层初始化时检查GGML_RPC_NO_RDMA,一旦设置即放弃 RDMA 探测。另外,transport.h 定义了单块最大 1 GiB 的MAX_CHUNK_SIZE,并说明flush()必须在每条消息边界调用——RDMA 传输会把多次写操作合并成定长帧,只有 flush 时才会把尾部不完整帧发出;TCP 上该调用为空操作。这正是两种传输可以共用同一套消息接口的关键。
协议与调试:从源码看 RPC 如何工作
命令集
ggml-rpc.cpp 定义了双方通信的全部 RPC 命令(rpc_cmd枚举),覆盖了远程计算所需的关键生命周期:
- 设备与内存:
RPC_CMD_DEVICE_COUNT(设备数量)、RPC_CMD_GET_DEVICE_MEMORY(设备显存)、RPC_CMD_ALLOC_BUFFER/RPC_CMD_FREE_BUFFER/RPC_CMD_BUFFER_CLEAR、RPC_CMD_GET_ALIGNMENT/RPC_CMD_GET_MAX_SIZE; - 张量传输:
RPC_CMD_SET_TENSOR、RPC_CMD_SET_TENSOR_HASH(哈希去重)、RPC_CMD_GET_TENSOR、RPC_CMD_COPY_TENSOR、RPC_CMD_INIT_TENSOR、RPC_CMD_MEMSET_TENSOR; - 图计算:
RPC_CMD_GRAPH_COMPUTE、RPC_CMD_GRAPH_RECOMPUTE(重算)、RPC_CMD_GET_ALLOC_SIZE(预估计算分配量)。
所有消息结构均使用#pragma pack(push, 1)紧凑打包(如 rpc_tensor 完整序列化ggml_tensor的维度、步长、算子与源张量引用),以减少网络传输体积。协议版本兼容通过RPC_CMD_HELLO握手消息中的 major/minor/patch 版本与能力位(conn_caps)协商,这也是 RDMA 能力能够“握手期自动探测”的基础。
故障排查
遇到连接、设备暴露或计算异常时,可开启 server 侧调试日志:
$ GGML_RPC_DEBUG=1 bin/ggml-rpc-serverggml-rpc.cpp 中,GGML_RPC_DEBUG环境变量一旦被设置,LOG_DBG宏就会输出GGML_LOG_DEBUG级别的详细消息,便于定位是哪一步 RPC 交互出了问题。此外,客户端在收到异常响应时会以 “Remote RPC server crashed or returned malformed response” 中止(RPC_STATUS_ASSERT),看到这个报错基本可以判定是对端 server 崩溃或响应格式损坏,应先在远程主机上用GGML_RPC_DEBUG=1复现。
小结:适用边界与最佳实践
- 拓扑:一台主控机 + N 台运行
ggml-rpc-server的远程主机;默认按显存比例自动分摊权重与 KV cache,需要精细控制时用--tensor-split覆盖。 - 部署清单:远程机
cmake .. -DGGML_CUDA=ON -DGGML_RPC=ON构建 → 以-H 0.0.0.0 -p 50052启动(注意默认只绑定 127.0.0.1)→ 主控机--rpc host1:port,host2:port接入。 - 性能手段:大模型加
-c启用本地缓存(目录受LLAMA_CACHE控制);高速网络环境可利用握手自动协商的 RDMA,必要时用GGML_RPC_NO_RDMA=1排除干扰。 - 边界提醒:RPC 后端仍处于概念验证阶段,接口与行为可能随版本变动,且不提供网络安全保证——仅限受信任网络内部实验使用,不要部署到公网或敏感环境。
【免费下载链接】llama.cppLLM inference in C/C++项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考