#01 基础原理
在前面的文章中,我们已经知道,shared memory 访存是通过 memory transaction (内存事务)进行的,一个 memory transaction 最长是 128 bytes。如果一个 warp 内同一时刻访存超过 128 bytes,则这一过程会被拆分为多个 memory transactions。访存是否产生 bank conflicts 是在单个 memory transaction 中判断的。
以最简单的每个 thread 读取一个不同的 float 数据为例,此时一个 warp 恰好访问 128 bytes 数据,不产生 bank conflicts 时只会产生一个 memory transaction。
当使用 float2 进行访存时,每个 thread 同时读取 8 bytes 数据,若每个 thread 读取不同的数据,则一个 warp 共访问 256 bytes 数据,不产生 bank conflicts 时会拆分为 2 个 memory transactions 进行,且拆分顺序是按照 0-15,16-31 这样 16 个连续 threads 为一组进行的,我们将每一组称为一个 half-warp,即 1/2 warp。
当使用 float4 进行访存时,每个 thread 同时读取 16 bytes 数据,若每个 thread 读取不同的数据,则一个 warp 共访问 512 bytes 数据,不产生 bank conflicts 时会拆分为 4 个 memory transactions 进行,且拆分顺序是按照 0-7,8-15,16-23,24-31 这样 8 个连续 threads 为一组进行的,我们将每一组称为一个 quarter-warp,即 1/4 warp。
#02 Shared Memory广播
当每个 thread 读取一个 float 数据时,如果一个 warp 中不同的 thread 读取相同的数据,会触发 broadcast (广播)机制,合并为一次访存。
当以 float2 进行访存时,在不触发 broadcast 机制的情况下,一个 warp 中有几个活跃的 half-warp 就会有几个 memory transactions。活跃是指 half-warp 中有任意 thread 活跃。此时,触发 broadcast 机制需要至少满足以下条件之一:
- 对于 warp 内所有活跃的第 i 号 thread,第 i xor 1 号 thread 不活跃或访存地址一致;
- 对于 warp 内所有活跃的第 i 号 thread,第 i xor 2 号 thread 不活跃或访存地址一致;
当触发 broadcast 机制时,两个 half-warp 可以合并产生 1 个 memory transaction。
当以 float4 进行访存时,在不触发 broadcast 机制的情况下,每个 half-warp 中有几个活跃的 quarter-warp 就会有几个 memory transactions。触发 broadcast 机制的条件与 float2 中描述的一致。
#03 线程排布分析
以下面的例子来分析内存访问情况,以float4为粒度进行访存。
在读取执行时,将Tile A的smem进行转置。
在读取 tileA 时,每个 quarter-warp 访问相同的 float4 数据,没有 bank conflicts,且会触发广播机制,整个 warp 中两两 quarter-warp 可以合并产生 1 个 memory transaction,共产生 2 个 memory transactions;
在读取 tileB 时,每个 quarter-warp 访问 8 个不同 bank 的 float4 数据,也没有 bank conflicts,但不会触发广播机制,整个 warp 会产生 4 个 memory transactions。
#04 线程排布优化
Z-Order是一种线程排布方式,将每个 quarter-warp 尺寸调整为 4×2,可以让 tileA 和 tileB 的读取过程都触发广播机制。示意图如下:
在读取 tileA 时,第 i 和 第 i xor 1 号 thread 访问相同元素,满足第一个条件,因此整个 warp 中两两 quarter-warp 可以合并产生 1 个 memory transaction,同样共产生 2 个 memory transactions;
在读取 tileB 时,第 i 和 第 i xor 2 号 thread 访问相同元素,满足第二个条件,因此整个 warp 中两两 quarter-warp 可以合并产生 1 个 memory transaction,共产生 2 个 memory transactions;
相比之下,Z-Order 排布的方式可以产生更少的 memory transactions,理论上更优。
由线程排布规律可以看到,Z-Order的排布顺序不止一种,下面的方式也是可以的。