-1

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

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

4

1 回答 1

5

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

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

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

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

给定的线程可以以任何模式访问共享内存,而无需担心或可能由于其自身的活动而发生共享内存回冲突。只有在单个 warp 中有 2 个或更多线程时才会出现银行冲突,这是由于在warp-wide 发出的特定共享内存指令或访问的结果。

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

假设我们指的是 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 ...

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

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

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

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

  • 如果我在 CUDA 代码中有一个循环,则在该循环的单独迭代之间不可能出现银行冲突
  • 如果我有两行单独的 CUDA C++ 代码,那么这两行单独的 CUDA C++ 代码之间就不可能发生银行冲突。
于 2020-01-07T20:46:29.787 回答