CANN hixl:大模型 PD 分离场景的零拷贝通信库

文章目录

  • 前言
    • [为什么 PD 分离需要专门的通信库](#为什么 PD 分离需要专门的通信库)
    • [先搞清楚:hixl 不是 hccl 的"轻量版"](#先搞清楚:hixl 不是 hccl 的"轻量版")
    • 逐层剥开:零拷贝到底零在哪
      • [RDMA 单边读写的底层机制](#RDMA 单边读写的底层机制)
      • [Prefill-Decode 流水线的时序优势](#Prefill-Decode 流水线的时序优势)
    • [hixl 在 CANN 架构中的位置](#hixl 在 CANN 架构中的位置)
    • 与其他仓库的协作关系
      • [与 runtime:设备与内存的基础设施](#与 runtime:设备与内存的基础设施)
      • [与 ops-transformer:KV Cache 的生产者](#与 ops-transformer:KV Cache 的生产者)
      • [与 ATB:推理部署的调度层](#与 ATB:推理部署的调度层)
      • [与 cann-recipes-infer:端到端推理配方](#与 cann-recipes-infer:端到端推理配方)
      • [与 hccl:互补而非竞争](#与 hccl:互补而非竞争)
      • ascend-boost-comm:不是通信库
    • 走向实践

前言

大模型推理时,Prefill 阶段把长 prompt 塞进去,Decode 阶段一个 token 一个 token 往外吐------这两个阶段的计算特征截然不同,硬塞在同一张卡上就是互相拖后腿。那拆开呢?拆到不同的 NPU 上,又冒出一个新问题:Prefill 产出的 KV Cache 怎么搬到 Decode 侧?昇腾 CANN 的 hixl 就是专门干这件事的------一个面向 PD 分离场景的单边通信库,核心能力是零拷贝 KV Cache 传输

为什么 PD 分离需要专门的通信库

大模型推理的负载特征天生"两头重":Prefill 阶段计算密度极高,对算力吞吐的需求远超单 token 的延迟要求;Decode 阶段每步只处理一个 token,计算量小但对延迟极度敏感。两张不同的卡各司其职,资源利用率自然更高------这就是 PD 分离的基本动机。

但分离之后,瓶颈从计算侧转移到了通信侧。以 70B 模型为例,单次请求的 KV Cache 容量随序列长度线性增长:

python 复制代码
# KV Cache 大小估算(以 LLaMA-70B 为例)
hidden_size, num_layers, num_heads, head_dim = 8192, 80, 64, 128
seq_len = 4096
elem_bytes = 2  # FP16
kv_gb = (2 * num_layers * num_heads * head_dim * seq_len * elem_bytes) / (1024**3)
print(f"KV Cache: {kv_gb:.1f} GB")  # KV Cache: 5.0 GB

5GB 的 KV Cache 在每次 Prefill 完成后都需要从 Prefill 节点搬到 Decode 节点。如果走传统的 CPU 中转路径,不仅占用大量 Host 内存带宽,还会引入毫秒级的额外延迟------对 Decode 阶段首 token 延迟(TTFT)的打击是致命的。

更关键的是,这个传输具有明确的方向性和点对点特征:数据只从 Prefill 流向 Decode,不需要多方协商,不需要全局同步。用集合通信来做这件事,等于让一个简单的投递任务变成了全员会议。

hixl 的设计动机: 为 PD 分离这一特定场景提供一条不走 CPU 的点对点直通通路,用单边操作替代集合操作,用零拷贝路径替代多跳拷贝路径。

先搞清楚:hixl 不是 hccl 的"轻量版"

这是最常见的误区。hixl 和 hccl 虽然都挂在 CANN 五层架构的第 4 层(昇腾计算执行层)的通信子层,但二者属于完全不同的范畴

text 复制代码
第4层:昇腾计算执行层 - 通信子层
  ├─ hccl:集合通信库(AllReduce/Broadcast/AllGather...)
  │        → 多方协商、同步完成
  └─ hixl:单边通信库(点对点零拷贝传输)
           → 一方发起、另一方无需参与

hccl 是集合通信------多张卡坐下来开个会,大家同步协商后才动手。hixl 是单边通信------Prefill 侧把 KV Cache 直接推过去,Decode 侧甚至不需要醒着。一个是"开会",一个是"快递"。

类比一下:hccl 像一群人搬家具,必须所有人到齐、步调一致才能抬;hixl 像快递柜,寄件人放进去,收件人随时取,两个人不需要同时在场。

从编程模型看,二者的差异更加本质:

c 复制代码
// hccl:集合通信 ------ 所有 rank 必须同时调用,否则死锁
HcclAllReduce(send_buf, recv_buf, count, dtype, reduce_op, comm, stream);

// hixl:单边通信 ------ 只有发起方调用,接收方无需参与
HixlRdmaWrite(remote_mr_handle, local_kv_ptr, byte_len);

PD 分离也不等于普通分布式。 普通分布式训练里,梯度同步是全局性的,每张卡都要跟所有其他卡交换数据;PD 分离里,KV Cache 的传输是方向确定的------只从 Prefill 流向 Decode,数据单向流动。用集合通信来做这件事,就像用搬家公司送一封信。

逐层剥开:零拷贝到底零在哪

KV Cache 是大模型推理中最"重"的中间数据。传统方式传输这么大的数据需要四次拷贝,两次经过 CPU:

text 复制代码
传统路径(4 次拷贝):Prefill 显存 → Host 内存 → 网卡 DMA → 对端 Host 内存 → Decode 显存
hixl 路径(0 次拷贝):Prefill 显存 → RDMA 直传 → Decode 显存

四次拷贝意味着:Host PCIe 带宽被占满、CPU 周期被浪费、延迟随数据量线性增长。对 5GB 的 KV Cache,仅 Host 侧的拷贝就消耗数十毫秒。

不经过 Host,不落盘,不拷贝。 数据从一张卡的显存直接飞到另一张卡的显存,CPU 甚至不知道这件事发生过。

RDMA 单边读写的底层机制

hixl 的零拷贝基于 RDMA(Remote Direct Memory Access)的单边操作语义。与传统 TCP 通信不同,RDMA 单边操作允许发起方直接读写远端内存,远端 CPU 不参与:

c 复制代码
// === hixl 单边通信完整流程 ===

// 1. 双方注册本地内存区域(Memory Region)
HixlMemReg(prefill_kv_buf,  cache_size, &prefill_mr);
HixlMemReg(decode_recv_buf, buf_size,   &decode_mr);

// 2. 交换 MR 信息(启动时一次性握手)
HixlExchangeMrInfo(decode_mr, remote_rank);

// 3. Prefill 侧:发起单边写,数据直接 Prefill 显存 → 网卡 → Decode 显存
HixlRdmaWrite(decode_mr, prefill_kv_buf, kv_cache_bytes);
HixlFlush(remote_mr);  // 等待远端完成确认

// 4. Decode 侧:直接使用已到达的数据,无需任何接收调用
launch_decode(decode_recv_buf, seq_len, num_layers);

注意第 5 步------Decode 侧的代码里没有一行通信调用。这正是单边通信的核心优势:接收方对传输过程完全无感,通信和计算可以在 Decode 侧完全重叠。

"零拷贝"的核心不在"快",而在"不搬"。 更快的搬运会降低延迟,但不搬运直接消除了一整类开销。这不是优化搬运,是消灭搬运本身。

Prefill-Decode 流水线的时序优势

零拷贝带来的不只是单次传输的延迟下降,更是整体流水线的吞吐提升。传统拷贝路径中,KV Cache 传输和 Decode 计算是串行的:

传统拷贝路径下,Decode 必须空等 KV Cache 传输完成才能开始;hixl 的流水线模式下,传输与 Decode 计算重叠执行,Decode 侧在已就绪的层数据到达后即可启动计算,后续层数据流水式到达、逐层解锁。

hixl 在 CANN 架构中的位置

复制代码
第1层  AscendCL(编程接口)
第2层  AOL 算子库 ← ops-transformer(KV Cache 算子)
第3层  Graph Compiler
第4层  Runtime ← hixl 在此运行
        ├─ hccl(集合通信)
        └─ hixl(单边通信)  ← 你在这里
第5层  驱动/硬件

hixl 跑在 runtime 之上------所有通信操作最终通过 runtime 的设备管理和内存管理接口执行。同时,hixl 传输的 KV Cache 数据由 ops-transformer 的 KV Cache 算子(如 GatherPAKVCache)生产,二者形成紧密的上下游协作。

第 4 层的通信子层同时承载 hccl 和 hixl,二者各管一块。hccl 处理 AllReduce、AllGather、Broadcast 等需要多 rank 同步的集合操作,主要服务于训练场景的梯度同步;hixl 处理 RDMA 单边读写等点对点操作,专门服务于推理场景的 KV Cache 传输。两者不重叠、不竞争,互补构成完整的通信能力。

与其他仓库的协作关系

复制代码
上游(hixl 依赖)
  ├─ runtime        → 提供设备/流/内存管理基础设施
  └─ ops-transformer → 生产 KV Cache 数据

下游(依赖 hixl)
  ├─ ATB(ascend-transformer-boost) → PD 分离推理部署
  └─ cann-recipes-infer              → PD 分离场景配方

关联(互补关系)
  ├─ hccl              → 集合通信,与 hixl 单边通信互补
  └─ ascend-boost-comm → 算子公共平台中间件(非通信库)

一个完整的 PD 分离推理链路:ops-transformer 算子产出 KV Cache → hixl 零拷贝传输到 Decode 侧 → ATB 调度 Decode 执行 → cann-recipes-infer 提供端到端配方。hixl 在这条链路中扮演"高速公路"的角色------它不生产数据,不消费数据,只负责让数据以最快速度到达目的地。

与 runtime:设备与内存的基础设施

runtime 为 hixl 提供底层资源管理能力。hixl 注册内存区域时,底层调用的是 runtime 的 Device 内存分配接口;hixl 的异步操作挂在 runtime 的 Stream 上执行:

c 复制代码
// hixl 依赖 runtime 的设备/流/内存管理
rtStream_t stream;
rtStreamCreate(&stream, 0);
void* dev_buf = nullptr;
rtMalloc(&dev_buf, cache_size, RT_MEMORY_HBM);
HixlRdmaWriteAsync(remote_mr, dev_buf, cache_size, stream);
rtStreamSynchronize(stream);

与 ops-transformer:KV Cache 的生产者

ops-transformer 中的 KV Cache 算子(如 GatherPAKVCache、RopeKVCache)负责在 Prefill 阶段生成和整理 KV Cache。hixl 拿到这些算子输出的设备内存指针后,直接注册为 RDMA Memory Region 并发起传输:

c 复制代码
// ops-transformer 产出 KV Cache(设备内存)
GatherPAKVCache(key_cache, value_cache, &kv_output, seq_len);

// hixl 注册并传输,零拷贝------不涉及任何 Host 侧拷贝
HixlMemReg(kv_output, kv_size, &kv_mr);
HixlRdmaWrite(decode_mr, kv_output, kv_size);

与 ATB:推理部署的调度层

ATB(ascend-transformer-boost)是昇腾 CANN 的大模型推理加速库,负责算子调度和执行优化。在 PD 分离部署中,ATB 负责:

  • 调度 Prefill 节点的计算图
  • 调度 Decode 节点的自回归循环
  • 在 Prefill 完成后触发 hixl 的 KV Cache 传输

ATB 内部封装了 hixl 的通信接口,用户在 ATB 的 Python 配置层面只需声明 PD 分离模式:

python 复制代码
# ATB PD 分离配置(示例)
atb_config = {
    "model": "llama_70b",
    "pd_separation": {
        "enabled": True,
        "prefill_devices": [0, 1, 2, 3],
        "decode_devices":  [4, 5, 6, 7],
        "kv_transfer": "hixl",  # 使用 hixl 零拷贝传输
        "pipeline_overlap": True,
    },
}

与 cann-recipes-infer:端到端推理配方

cann-recipes-infer 提供具体模型(LLaMA、Qwen、DeepSeek 等)的端到端推理部署方案。其中的 PD 分离配方集成了 hixl + ATB + ops-transformer 的完整链路:

bash 复制代码
# cann-recipes-infer 启动 PD 分离推理
cd cann-recipes-infer/llama/pd_separation

# Prefill 节点(0-3 号卡)
bash run_prefill.sh --model_path /data/llama-70b --devices 0,1,2,3 --kv_transfer hixl

# Decode 节点(4-7 号卡)
bash run_decode.sh --model_path /data/llama-70b --devices 4,5,6,7 --kv_transfer hixl --prefill_addr 192.168.1.10:29500

# profiling KV Cache 传输耗时
msprof --output=./profiler_log --application="./pd_infer_server --kv_transfer hixl" --duration=60

与 hccl:互补而非竞争

hccl 和 hixl 共存于 CANN 第 4 层通信子层,各自覆盖不同的通信模式。在 PD 分离推理中,hixl 负责 Prefill→Decode 的 KV Cache 点对点传输;如果 Decode 侧采用多卡并行(Tensor Parallel),Decode 内部的权重同步仍然由 hccl 处理。两者协同工作,各司其职。

ascend-boost-comm:不是通信库

⚠️ ascend-boost-comm 是算子公共平台中间件,实现 M×N 算子复用------即一个算子适配多种硬件后端。它属于算子层的基础设施,与通信无关。不要把它和 hixl、hccl 混为一谈。

走向实践

如果你正在做 PD 分离部署,hixl 是绕不开的一环。可以从这里开始:

bash 复制代码
# 克隆、构建、测试
git clone https://atomgit.com/cann/hixl.git && cd hixl
mkdir build && cd build && cmake .. && make  # 构建产物: libhixl.so

# 运行通信测试(验证 RDMA 通路)
./tests/hixl_rdma_test --device 0 --peer_device 1 --size 5368709120
# 输出: RDMA Write: 5.0 GB in 1.2 ms (bandwidth: 40.2 GB/s)

# 用 npu-smi 检查带宽------hixl 零拷贝下 Host 内存带宽应几乎为零
npu-smi info -t board -i 0

更完整的 PD 分离部署方案,参考 cann-recipes-infer 中针对具体模型的推理配方,其中集成了 hixl + ATB + ops-transformer 的端到端流程。

仓库地址:https://atomgit.com/cann/hixl

相关推荐
z200509301 小时前
今日算法(组合问题III)(回溯的使用)
java·算法·leetcode
XiYang-DING1 小时前
【Java EE】IPv6
java·java-ee·php
Re_zero1 小时前
从乐观锁被冲烂到原子扣减稳如磐石:高并发防超卖方案的三次迭代
java·后端
落木萧萧8251 小时前
自动生成 SQL 会拖慢性能吗?实测 MyBatisGX、MyBatis、MyBatis-Plus、MyBatis-Flex
java·orm
Full Stack Developme1 小时前
Spring Boot 状态机 与 com.alibaba.cola 中的状态机
java·spring boot·后端
MacroZheng1 小时前
让 Claude Code 成本爆降 89%,这个开源工具有点猛...
java·人工智能·后端
likerhood1 小时前
Java 异常处理:从 try-catch-finally 到项目最佳实践
java·开发语言·php
咕噜咕噜啦啦2 小时前
从spring到spring boot——JAVA项目开发
java·前端·spring boot·后端·spring
共绩算力2 小时前
无服务器冷启动:HF 缓存与预计算哈希
人工智能·缓存·serverless·哈希算法·共绩算力