CUDA 上共享内存中的非顺序访问引起的 Bank 冲突

Bank Conflicts From Non-Sequential Access in Shared Memory on CUDA

我正在 CUDA 中针对 Volta 和 Turing 系列卡编写一些具有短程交互的 N 体模拟代码。我计划使用共享内存,但我不太清楚这样做时如何避免银行冲突。由于我的交互是本地的,我计划将我的粒子数据分类到本地组中,我可以将这些组发送到每个 SM 的共享内存(还不用担心有一个邻居正在从另一个 SM 处理的粒子。为了变得更好性能(避免内存冲突),仅每个线程 reads/writes from/to 一个不同的共享内存地址是否足够,但每个线程可以非顺序地访问该内存而不会受到惩罚?

我看到的所有信息似乎只提到内存被合并用于从全局内存到共享内存的复制,但我没有看到任何关于 warp(或整个 SM)中的线程是否关心共享内存中的合并。

In order to get good performance (avoid bank conflicts), is it sufficient only that each thread reads/writes from/to a different address of shared memory, but each thread may access that memory non-sequentially without penalty?

bank 冲突只可能在执行共享内存访问的单个 warp 中的线程之间发生,并且只能在每条指令(已发出)的基础上发生。我在这里说的指令是 SASS(GPU 汇编代码)指令,但是应该可以直接从 CUDA C++ 源代码中的共享内存引用中识别出来。

不存在银行冲突这样的想法:

  • 不同 warp 中的线程之间
  • 不同(发出的)指令引起的共享内存访问之间

一个给定的线程可以以任何模式访问共享内存,而不用担心共享内存反向冲突的可能性,因为它自己 activity。银行冲突仅由于单个 warp 中的 2 个或更多线程,作为 特定共享内存指令或访问的结果,在整个 warp 范围内发出 .

此外,每个线程 reads/writes from/to 不同的地址 是不够的。对于给定的已发布指令(即给定的访问),粗略地说,warp 中的每个线程必须从不同的 bank 读取,否则它必须从与另一个相同的地址读取经线地址(广播)。

假设我们指的是 32 位库,以及 32 个库的排列。 共享内存很容易想象成二维排列:

Addr                Bank
 v     0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

 0     0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32    32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
64    64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
96    96 97 98 ...

我们看到 addresses/index/offset/locations 0, 32, 64, 96 等在同一银行。地址 1、33、65、97 等在同一银行中,依此类推,对于 32 个银行中的每一个银行。当共享内存的地址在此二维排列中可视化时,存储区就像位置列

对发出给 warp 的给定指令(加载或存储)的非存储体冲突访问的要求是:

  • warp 中没有 2 个线程可以访问同一 bank/column 中的位置。
  • 如果同一列中的位置实际上是同一位置,则存在特殊情况。这会调用广播规则并且不会导致银行冲突。

并以稍微不同的方式重复上面的一些陈述:

  • 如果我在 CUDA 代码中有一个循环,则在该循环的单独迭代之间不可能出现库冲突
  • 如果我有两行单独的 CUDA C++ 代码,那么这两行单独的 CUDA C++ 代码之间就不可能出现库冲突。