来自 CUDA 上共享内存中的非顺序访问的银行冲突

Ian*_*ham -1 cuda bank-conflict gpu-shared-memory

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

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

Rob*_*lla 5

为了获得良好的性能(避免库冲突),仅每个线程从/向共享内存的不同地址读取/写入就足够了,但每个线程可以无序访问该内存而不会受到惩罚?

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

没有银行冲突这样的想法:

  • 在不同经线的线之间
  • 在由不同(发出)指令引起的共享内存访问之间

由于其自身的活动,给定线程可以以任何模式访问共享内存,而无需担心或可能发生共享内存回冲突。Bank 冲突仅由单个 warp 中的 2 个或更多线程引起,由于特定的共享内存指令或访问,在整个 warp 范围内发出

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

让我们假设我们指的是 32 位 bank,以及 32 个 bank 的排列。共享内存可以很容易地想象为一种二维排列:

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 ...
Run Code Online (Sandbox Code Playgroud)

我们看到地址/索引/偏移量/位置 0、32、64、96 等在同一组中。地址 1、33、65、97 等在同一个 bank 中,依此类推,对于 32 个 bank 中的每一个。当共享内存的地址在这种 2D 排列中可视化时,银行就像位置列

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

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

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

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