Skip to main content

exp7: CUDA 优化 (global memory, shared memory)

Course WorkCUDAIntroduction to High Performance ComputingAbout 3 minAbout 831 words

分析 test_gmem.cu 的性能变化来源

__global__ void stride_copy(float *dst, float *src) {
  int i = blockIdx.x * blockDim.x + threadIdx.x;
  dst[i * STRIDE] = src[i * STRIDE];
}
Global Memory
Global Memory

性能变化的主要来源是 GPU 的哪种机制引起的?

访存合并机制.

这种机制如何影响该程序的性能?

在 DRAM, L2 cache, L1 cache 之间传输数据的最小单元为 1 sector = 32 Bytes. DRAM 访问总量为 size * STRIDE * sizeof(float)STRIDE 成正比. 因此 STRIDE 越大, 程序访问的 transaction 个数越多, 且近似呈正比, 导致性能反比下降.

是否有其他的硬件功能参与了该程序的执行过程, 它们会如何影响该程序的执行效率?

cache. 顺序访问时, 数据会被缓存在 cache 中, 从而减少对 DRAM 的直接访问, 提升性能.

分析 test_smem.cu 的性能变化来源

__global__ void test_shmem() {
#if (BITWIDTH == 2)
  volatile __shared__ uint16_t shm[32 * 128];
  volatile uint16_t tmp;
#elif (BITWIDTH == 4)
  volatile __shared__ uint32_t shm[32 * 128];
  volatile uint32_t tmp;
#elif (BITWIDTH == 8)
  volatile __shared__ uint64_t shm[32 * 128];
  volatile uint64_t tmp;
#endif

  for (int i = 0; i < times; i++) {
    tmp = shm[threadIdx.x * STRIDE];
  }
}
Shared Memory
Shared Memory

固定 BITWIDTH 时, 程序的性能变化来源于哪种硬件机制?

bank conflict.

BITWIDTH 设置为 2,8 时性能变化的趋势相比于 BITWIDTH 设置为 4 时有什么不同之处, 请解释.

BITWIDTH 设置为 4 时, bandwidth 随着 STRIDE 的增大而减小. BITWIDTH 设置为 2 时, STRIDE 为 1 和 2 时 bandwidth 几乎相等, 而后变化趋势与 BITWIDTH 设置为 4 时基本相同. BITWIDTH 设置为 8 时, STRIDE 为 16 和 32 时 bandwidth 几乎相等, 在这之前的变化趋势与 BITWIDTH 设置为 4 时基本相同.

BITWIDTH 为 4 时, 访存粒度为 4 Bytes = 1 bank, 相邻两个 thread 访问地址间隔为 STRIDE 个 bank. 当 STRIDE 为 1 时, 同一 wrap 内的 32 个 thread 访问了第一行内的 32 个 bank, 不存在 bank conflict. 当 STRIDE 为 2 时, threadIdx.xthreadIdx.x + 16 访问同一 bank, 发生 2-way bank conflict. 当 STRIDE 为 4 时, threadIdx.x, threadIdx.x + 8, threadIdx.x + 16, threadIdx.x + 24 访问同一个 bank, 发生 4-way bank conflict. 类似的, STRIDE 为 8 和 16 时分别发生 8-way bank conflict 和 16-way bank conflict.

BITWIDTH 为 2, STRIDE 为 1 时, 相邻两个 thread 访问同一个 bank, 发生 2-way bank conflict, 共访问 16 个 bank. 而 STRIDE 为 2 时, 虽然没有 bank conflict, 但需要访问 32 个 bank. 但由于每次访问仅使用 0.5 bank, 带宽为理论峰值的一半. 因此 STRIDE 为 1 或 2 时性能相近.

BITWIDTH 为 8, STIRDE 为 16 时, 相邻两个 thread 访存地址间隔为 128 Bytes, 恰为一行. 这意味着, 所有 thread 都访问 bank-0 和 bank-1, 发生 32-way bank conflict. 而当 STRIDE 为 32 时, 相邻两个 thread 访存地址间隔为 256 Bytes, 为两行, 此时所有 thread 仍都访问 bank-0 和 bank-1, 发生 32-way bank conflict. 因此 STRIDE 为 16 或 32 时性能相近.

Performance

Global Memory

StrideBandwidth (GB/s)
1530.015
2182.471
491.9932
846.2866

Shared Memory

BitwidthStrideBandwidth (GB/s)
214258.05
224270.88
242149.69
28831.405
216427.135
232215.022
418607.33
424315.77
442027.46
481012.86
416504.37
432251.766
818657.57
824339.44
842173.55
881087.65
816544.069
832544.068