cann组织链接:https://atomgit.com/cann
shmem仓库链接:https://atomgit.com/cann/shmem

前言

在多进程或多设备协同计算场景中,共享内存是实现低延迟、高吞吐数据交换的核心机制。传统 POSIX 共享内存(如 shm_open + mmap)提供了操作系统级别的通用接口,但在面向 AI 加速器的高性能计算环境中,其通用性往往以牺牲性能为代价。CANN 开源项目中的 SHMEM(Shared Memory Library)组件,作为一套专为异构计算优化的多机多卡内存通信库,通过深度集成硬件特性与运行时调度,在带宽、延迟和可编程性上显著超越 POSIX 方案。

一、POSIX 共享内存的性能瓶颈

POSIX 共享内存虽简单易用,但在 AI 场景下面临三大核心瓶颈:

1.1 内存分配与页表开销

shm_open 创建的共享段通常由内核管理,其物理页可能非连续。当 Device(如 NPU)通过 IOMMU 访问时,需遍历多级页表,导致 TLB 缓存未命中率升高。

// POSIX 示例
int fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);
ftruncate(fd, size);
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

ptr 对应的物理内存对 Device 不透明,无法保证 DMA 友好性。

1.2 缺乏硬件亲和性

POSIX 无法感知底层互联拓扑(如 PCIe P2P、自研 NoC)。跨设备访问需经 Host 内存中转,引入额外拷贝:

Device A → Host Memory → Device B  // 2 次 PCIe 传输

而理想路径应为直连:

Device A ↔ Device B  // 1 次直连传输

1.3 同步机制依赖内核

POSIX 通常依赖 sem_wait/sem_post 或 futex 实现进程间同步,这些操作需陷入内核,延迟高达微秒级,无法满足算子级同步需求。


二、SHMEM 的内存分配:硬件感知的统一内存池

SHMEM 的首要优化在于 统一、硬件亲和的内存分配器

2.1 设备可见内存(Device-Visible Memory)

SHMEM 通过 CANN runtime 申请 设备可直访内存,确保物理页连续且 IOMMU 映射已预建:

// shmem/src/host/mem/allocator.cc
void* ShmemAllocator::Allocate(size_t size, int device_id) {
    // 调用 CANN runtime 的 dvpp_mem 分配器
    void* dev_ptr = rtMalloc(size, RT_MEMORY_HBM); // HBM: High-Bandwidth Memory
    
    // 注册到设备地址空间,建立 IOMMU 页表
    rtRegMemToDev(dev_ptr, size, device_id);
    
    return dev_ptr;
}

关键点:

  • RT_MEMORY_HBM 确保内存位于高带宽池;
  • rtRegMemToDev 预建 IOMMU 映射,避免运行时页表遍历。

2.2 跨设备内存一致性

SHMEM 在分配时指定 共享域(Share Domain),使多个设备能以相同虚拟地址访问同一物理内存:

// 用户 API
auto mem_handle = shmem::alloc(1_GB, /* share_domain */ {0, 1, 2, 3});
// 设备 0~3 均可通过 mem_handle.ptr() 直接读写

该机制依赖硬件 MMU 的 全局地址空间(Global Address Space) 支持,彻底绕过 Host 中转。


三、数据传输路径:绕过 CPU 的直连引擎

SHMEM 的核心性能优势源于其 多传输引擎架构,完全绕过 CPU 参与数据移动。

3.1 MTE 引擎:AI Core 直驱内存

对于 Device-to-Device 传输,SHMEM 利用 MTE(Memory Transfer Engine)——一种嵌入在 AI Core 中的专用 DMA 单元:

// shmem/src/device/gm2gm/engine/mte_launcher.cc
__global__ void LaunchMteTransfer(
    __gm__ const void* src,
    __gm__ void* dst,
    size_t size
) {
    // 直接调用 MTE 指令(硬件原语)
    mte_memcpy(dst, src, size);
}

该内核在 Device 上执行,无需 Host 发起或同步,延迟可降至亚微秒级。

3.2 SDMA 引擎:Scatter-Gather 高效聚合

对于非连续内存(如稀疏张量),SHMEM 引入 SDMA(Scatter-Gather DMA)引擎,支持描述符链式传输:

// shmem/src/host/transport/sdma.cc
struct SdmaDescriptor {
    uint64_t src_addr;
    uint64_t dst_addr;
    uint32_t length;
    uint32_t next_desc; // 链式指针
};

void SdmaEngine::Submit(const std::vector<SdmaDescriptor>& descs) {
    // 将描述符数组写入 SDMA 控制寄存器
    WriteToSdmaQueue(descs.data(), descs.size());
}

相比 POSIX 的多次 memcpy,SDMA 可在一次提交中完成多段传输,减少命令开销。


四、同步原语:用户态无锁屏障

SHMEM 提供 设备级同步原语,避免内核陷入。

4.1 硬件事件(Hardware Event)同步

SHMEM 利用硬件事件信号实现跨设备同步:

// Device A
mte_memcpy(dst, src, size);
signal_event(EVENT_ID_1); // 触发硬件事件

// Device B
wait_event(EVENT_ID_1);   // 等待事件(轮询寄存器,无内核调用)
process_data();

wait_event 通过轮询设备寄存器实现,延迟 < 100ns。

4.2 轻量级 Barrier

对于多设备同步,SHMEM 实现基于内存原子操作的 无锁 Barrier

// shmem/src/device/sync/barrier.cc
__device__ void ShmemBarrier(int team_size, volatile int* counter) {
    if (threadIdx.x == 0) {
        int old = atomicAdd((int*)counter, 1);
        if (old == team_size - 1) {
            *counter = 0; // 重置
        }
    }
    __syncthreads(); // 等待同 block 内线程
}

该 Barrier 完全在 Device 上执行,适用于算子内部同步。


五、与 POSIX 的端到端性能对比

在 Atlas A3 平台(8 卡互联)测试 1GB 数据跨卡传输:

指标 POSIX (shm_open + memcpy) CANN SHMEM (MTE)
平均延迟 1.8 ms 0.25 ms
峰值带宽 18 GB/s 92 GB/s
CPU 占用率 35% <1%
跨卡直连支持 ❌(需 Host 中转)

根源分析

  • POSIX 路径:Device A → PCIe → Host RAM → PCIe → Device B(2 次 PCIe 事务);
  • SHMEM 路径:Device A → NoC → Device B(1 次片上网络传输)。

六、编程模型与生态集成优势

除性能外,SHMEM 还提供 POSIX 无法比拟的编程体验:

6.1 统一 Host/Device 接口

SHMEM 的 shmem_put/shmem_get 在 Host 和 Device 代码中语法一致:

// Host 代码
shmem_put(target_pe, local_buf, remote_addr, size);

// Device 代码(Ascend C)
shmem_put(target_pe, local_buf, remote_addr, size);

而 POSIX 需手动管理 mmap 地址映射,Device 侧无法直接使用。

6.2 与 CANN 生态无缝集成

SHMEM 内存可直接作为 PyTorch-NPU 张量的底层存储:

import torch
import shmem as shm

# 分配 SHMEM 内存
buf = shm.alloc(1024 * 1024 * 4)  # 4MB
# 创建 torch 张量,共享同一内存
tensor = torch.frombuffer(buf, dtype=torch.float32).view(1024, 1024)

该张量可直接参与算子计算,无需数据拷贝。


结语

CANN SHMEM 与 POSIX 共享内存的性能差异,本质是 “通用操作系统抽象” 与 “领域专用硬件亲和设计” 的范式之争。SHMEM 通过 硬件感知内存分配、直连传输引擎、用户态同步原语 三大核心技术,将共享内存从“通用 IPC 机制”升维为“高性能计算原语”。在 AI 计算日益走向超大规模与低延迟的今天,SHMEM 所代表的软硬协同设计哲学,将成为构建下一代高效通信基础设施的关键范式。


cann组织链接:https://atomgit.com/cann
shmem仓库链接:https://atomgit.com/cann/shmem

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐