Fast H2D 算子优化记录

背景

该算子是 KV Offload的核心执行算子之一,以 evict_ids 指定的 (src_idx, dst_idx) 为索引,从 CPU pinned memorykv_cpu 的张量中读取每个

token (固定 656 字节),逐字节拷贝到 GPU 的 sparse_gpu 这个张量里。初版实现上是单线程/单 token 的 UVA 读写。

这里需要先搞清楚两个概念:

  1. 什么是 CPU 的 pinned memory?
  2. 什么是 UVA 读写?

这里给出精简的回答:

进一步的,我们会思考这么一个问题:这里的原版本的实现中,为何要用略显繁琐的UVA,而不是看起来更简单的UVM 呢?

[!note]

因为我们这个算子处理的是稀疏化的索引 gather 操作,几乎不存在空间局部性,导致直接换页的开销过大,不如 UVA 精准地命中某一个 token 的数据

BaseLine 的代码分析

结合背景的介绍,我们已经得知了这个算子的语义,以及算子要使用的基本原语。那么剩下的就剩下使用 NCU 工具,具体地分析一下这个算子,实际表现出来的性能如何,以及报告中提到的缺陷是否能够和代码一一对应上。

代码形态 (Naive Byte-Copy):

// 伪代码:每个线程处理一个 Token
__global__ void baseline_kernel(...) {
    int token_idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (token_idx >= total_copies) return;
    // 逐字节拷贝,产生大量细碎 PCIe 事务
    uint8_t* s = src + src_idx * 656;
    uint8_t* d = dst + dst_idx * 656;
    for (int i = 0; i < 656; ++i) {
        d[i] = s[i]; 
    }
}

使用了附录中的 bench 脚本,对初始版本的代码进行了测试,测试结果如下: