为了获得良好的性能(避免银行冲突),仅每个线程从/向共享内存的不同地址读取/写入就足够了,但是每个线程可以不按顺序访问该内存而不会受到惩罚?
银行冲突只可能在单个 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++ 代码之间就不可能发生银行冲突。