内存模型
本文聚焦各类型内存的特性、使用方式、访问优化原则。
内存层次回顾
MUSA 内存系统包含多个层次,各层次在访问速度、容量和可见范围上存在差异:
访问速度 ↑
│ 寄存器 (Register) - 每线程私有,1 周期
│ 共享内存 (Shared Memory) - Block 内共享,1-2 周期
│ L1/L2 缓存 - 缓存加速
│ 常量内存 (Constant) - 只读,缓存命中时快
│ 全局内存 (Global) - 所有线程可访问,100+ 周期
↓

基础概念见 GPU 并行计算基础。
寄存器(Register)
特性
| 特性 | 说明 |
|---|---|
| 速度 | 最快(1 周期) |
| 容量 | 每线程有限(由编译器分配) |
| 可见范围 | 线程私有 |
| 生命周期 | 线程存在期间 |
使用方式
// 编译器自动分配寄存器
__global__ void kernel() {
float a, b, c; // 通常存储在寄存器中
c = a + b;
}
寄存器溢出
当寄存器不足时,编译器会将变量溢出到局部内存(位于全局显存),性能显著下降:
// 可能导致寄存器溢出
__global__ void registerSpill() {
float temp[100]; // 大数组容易溢出
for (int i = 0; i < 100; i++) {
temp[i] = data[i];
}
}
共享内存(Shared Memory)
特性
| 特性 | 说明 |
|---|---|
| 速度 | 快(1-2 周期,片上内存) |
| 容量 | 每线程块有限(通常 48-96KB) |
| 可见范围 | 线程块内线程共享 |
| 生命周期 | 线程块存在期间 |
使用方式
__global__ void sharedMemKernel(float* data) {
__shared__ float shared_data[256]; // 显式声明
// 线程块内线程协作
shared_data[threadIdx.x] = data[threadIdx.x];
__syncthreads(); // 同步,确保所有线程完成写入
// 访问其他线程的数据
float neighbor = shared_data[threadIdx.x + 1];
}
全局内存(Global Memory)
特性
| 特性 | 说明 |
|---|---|
| 速度 | 慢(100+ 周期) |
| 容量 | 大(GB 级,GPU 显存) |
| 可见范围 | 所有线程可访问 |
| 生命周期 | 应用程序存在期间 |
使用方式
// 通过 musaMalloc 分配
float* d_data;
musaMalloc(&d_data, size * sizeof(float));
// Kernel 中访问
__global__ void globalMemKernel(float* data) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
data[idx] = data[idx] * 2.0f;
}
// 释放
musaFree(d_data);
NOTE: musaMalloc 和 muMemCreate 在申请完毕时,其内容不保证清零。
常量内存(Constant Memory)
特性
| 特性 | 说明 |
|---|---|
| 速度 | 快(缓存命中时) |
| 容量 | 较小(通常 64KB) |
| 可见范围 | 所有线程可读 |
| 生命周期 | 应用程序存在期间 |
使用方式
// 声明常量内存
__constant__ float coefficients[10];
__global__ void constantMemKernel(float* data) {
// 所有线程读取相同的常量值
float result = data[threadIdx.x] * coefficients[0];
}
系统内存(Host Memory)
系统内存是位于 CPU 端的内存,MUSA 提供多种类型优化 Host-Device 数据传输。
特性对比
| 内存类型 | 分配 API | 传输方式 | 性能特点 |
|---|---|---|---|
| 可分页内存(Pageable Memory) | malloc() | 同步拷贝 | 阻塞 CPU,性能低 |
| 锁定内存(Pinned Memory) | musaMallocHost() | 异步拷贝 | 支持 PCIe 并行传输 |
| 映射锁定内存(Mapped Pinned Memory) | musaMallocHost(..., musaHostAllocMapped) | 零拷贝访问 | 无需拷贝,直接访问 |
| 写合并内存(Write-Combined Memory) | musaMallocHost(..., musaHostAllocWriteCombined) | 异步拷贝 | 优化写入带宽 |
可分页内存(Pageable Memory)
传统的 malloc() 分配内存,可能被操作系统交换到磁盘:
// 慢:使用 pageable memory
float* h_data = (float*)malloc(size * sizeof(float));
musaMemcpy(d_data, h_data, size, musaMemcpyHostToDevice); // 同步,阻塞 CPU
// 问题:
// 1. musaMemcpy 前需要先分配到临时 pinned buffer
// 2. 额外的内存拷贝开销
// 3. 无法与 kernel 执行重叠
锁定内存(Pinned Memory)
物理内存被锁定,不会被操作系统交换,支持异步传输:
// 快:使用 pinned memory
float* h_pinned;
musaMallocHost(&h_pinned, size * sizeof(float));
// 异步拷贝,支持并发
musaStream_t stream;
musaStreamCreate(&stream);
musaMemcpyAsync(d_data, h_pinned, size, musaMemcpyHostToDevice, stream);
// 优势:
// 1. 直接 PCIe 传输,无需临时 buffer
// 2. 支持异步拷贝
// 3. 可与 kernel 执行重叠