WARP内无BANK CONFLICT即为最高效率,这是普通访问的判断准则。
不知道对于原子操作是不是同样?
BLOCK内很多WARP密集的访问无BANK 冲突的128字节的数据,与有冲突但分布在16K字节的范围,哪种方式效率更高?
影响因素有大有小,通常而言,原子操作对性能的负面影响要超过存储体冲突,因此应当首先优化原子操作。你的例子中有可能是存储体冲突的影响超过原子操作,这又当别论。
正如2#所言,LZ您需要权衡原子操作带来的性能损失和存储体冲突带来的性能损失。
您在顶楼说的“有冲突但分布在16K字节的范围”这里,分布在16KB里面的时候,原子操作的密集程度(这里表示向同一个地址原子操作的次数)是否也随着分布范围的扩大而缩小了相应的倍数?如果是的话,我觉得您可以尝试一下。(考虑到完全bank conflict是1/32的存储速度,而128B vs 16KB 按照上面的假设是128倍的原子操作密度。所以主观猜测也许后者分布16KB内会好一些。)
以及,您也可以考虑您的算法是否可以不用/少用原子操作,比如用规约操作代替。
个人看法仅供参考,祝您元宵节愉快~
是这样,规约,或者其他方法是可行的。但是计算代价要翻上若干倍,共享存储器的访问次数也要若干倍。不晓得到底值不值得。
最简单的原子操作的避免方法就是写操作时附带上线程标记,同步后检测,反复直到发现是自己设置的值。原子操作是硬件实现,我想无论如何也比自己手工软原子操作效率要好吧?
LZ您好,既然修改算法代价过大,那么我们继续讨论原子操作。
这里需要指出的是,前面我在3#的时候,考虑有些偏差,当时考虑原子操作实际上是维护多个线程串行操作,所以如果分散了,每个地址串行操作的线程数量会降低。但是考虑到您这个是shared memory上的原子操作,情况有所不同。
1:根据昨晚某测试的结果,shared memory上的原子操作实际上是展开成多条软件指令来实现的。大致为读取并锁定——检查锁定成功与否,如果不成功则goto回去继续读取并锁定操作;若成功——进行计算,回写数据并解锁。换言之,是多条指令实现的原子操作。(这一点不如global memory上的原子操作,global memory上是硬件实现的)
2:根据某推断,在shared memory上完成“读取并锁定”的时候,是逐bank锁定的。(事实上shared memory操作也是以bank为单位的,如果有两个同bank的不同地址的数据同时需要访问会造成 bank conflict)
这样的话,即使我们将需要原子操作的地址分布在16KB的shared memory里面,当bank0 里面A地址的数据没有解锁之前,bank0不能为同样在该bank里面的B地址的数据服务。所以3#中考虑带来的好处其实并不存在了。
而同时,bank conflict是一定会影响效能的。
综上,如果您一定要使用shared memory上的原子操作,那么请使用没有bank conflict的那种实现。
否则,如果并非一定要使用shared memory上的原子操作,您还可以尝试使用global memory上的原子操作,或者改写算法减少原子操作数量。
大致如此,个人观点供您参考。
请其他网友、斑竹、NV原厂支持予以补充。
预祝LZ元宵佳节愉快~
善哉。 感谢ICE大为我们带来的一篇长文。
是的,我深刻赞同ICE观点,并再次强调指出:
实际上在shared memory上是没有硬件支持的“原子操作”的。
和普通的硬件实现的global memory上的atomic ops不同,shared memory上的操作是循环实现的锁定-修改-写入解锁的。一般需要好几条指令,效率较低。
以及,ICE上文说过的,
bank conflict一定会影响shared memory的atomic ops的性能的!
所以ICE的观点我也深刻赞同:
楼主如果真的要用,最好选择无bank冲突的,而不是选择大范围内,冲突严重的。实际上对shared memory来说,拉开距离访问没啥用。(这不是global memory / L2, 你得拉开一定距离才能展现L2的整体实力(对于合并访问))。
以及ICE提出的引申:
如果能不用shared memory上的原子操作,改成其他保证逻辑正确的实现未尝不可,例如可以考虑warp vote等来杜绝或者哪怕减少原子操作的数目。
再次ICE为我们带来的长文,然后实际上,此文是对上文的强调和重复。我只是给变换了点说法而已。但这种变换,表达了对ICE的赞美,以及,希望楼主能自己思考下。
那个,还得说一下,2.0我是严重不推荐用shared memory上的原子操作的(你可以考虑用gloabl memory上的atomic*替代,或者用其他方式替换)。
2.1和3.x楼主还是可以考虑试试看效果的。
OK,明白了。结论就是WARP内无BANK冲突即为最高效率。无论普通访问还是原子操作都一样。
我想问一下,就是假设我可以做到访问无冲突,那么我变换算法,共享存储器访问次数会增加16倍左右。
那么这16倍的带宽换来去除原子操作代价值得吗?
实际上也就是哪种方式更好一些?
有没有原子指令的吞吐率值?与非原子指令吞吐率的比率是多少?我好评估一下怎样弄划算。
估计不合算!
普通的,例如说atomic在shared memory上,会被展开为一个循环:
其中有2条是读取并锁定bank + 回写并解锁,然后加法是2者中间读取完后普通加法的。然后循环,让每个竞争的线程都有1次机会完成这个循环体。
不考虑控制转移的开销,这5条指令(读1,写1,计算1,循环控制2), 实际上你可以看出,对shared memory读写只有2条,如果你为了规避原子操作而放大访问到16次的话,我估计是不合算的。
对于您的第二个问题,上文说过了,实际上shared memory无原子操作硬件实现,既然无硬件实现的原子操作指令,自然谈不上什么吞吐率了。(如前文,他们是普通的的读-修改-写入的过程,带有锁定而已,而不是存在一“真的”原子指令)。
我建议你仔细阅读ICE大的说法,根据您最后一个问题,您没有阅读5#.
“读取与锁定”是一个反复的过程,如果不成功,是不是就会第二次,第三次的“读取与锁定”?
这个锁定的过程不产生带宽?
我理解的这个过程效率低原因是竞争的随机性,是吧?锁定的线程会占用一小段时间,直到调度器调度到“回写并解锁”这条指令才算完事,其它线程在这段时间内就会浪费在试探过程中,占用调度器资源,并且做无用功。
(1)是的。大家在反复尝试,而只有其中的1个的幸运儿(不考虑kepler)会成功。
读取并锁定需要和shared memory交互数据,自然占用带宽。
(2)竞争是否是随机过程,是否某些线程占上应,这个不知道内部具体是如何实现的,可能是随机的,但不一定。 你可以咨询NVIDIA官支持,看看他们能否给您具体内部实现。
(3)是的,会占用一小部分时间。此间的确如您所说,其他人在反复尝试,并做无用功。但是就是这么实现的。您可以将您的建议发送给NVIDIA官方支持,看看新卡能否改进。
此外,要注意shared memory有32个bank, 如果不是集中在1个元素上进行原子操作,理论说,最多可以有32个幸运儿的。不惨。
多希望原子操作更高效啊。用原子操作算法更简洁,绕开的话算法复杂而且可能得不偿失。
不知道有没有什么测试得到的结果如何?比如实测比普通访问慢多少倍(还是会有不稳定的结果)?
新的硬件开普勒也是这样低效吗?
LZ您好
1:原子操作是比较简洁,但是原子操作本质上是一种强制串行操作,不可能特别快的,适当地修改算法减少或者消除原子操作不失为一种解决问题的方法。至于是否得不偿失,从开发成本上讲,是会更为麻烦一点;从执行性能上说,有可能会得到提升,这个要看具体实现。
2:至于和普通访存相比,这个没有可比性。普通访存是一条访存指令,而shared memory上的原子操作是5条指令,其中包括2次访存和另外的指令(前面有叙述)。此外其他线程还会不断循环申请,以及如果有bank conflict还会降低。以及因为原子操作,可能会导致其他线程也卡在这里这个较慢的访存上,从而整体上使得一些计算单元处于空闲状态,降低性能等。
实际情况是较为复杂的,有多个因素影响,无法直接告诉您哪个比哪个慢多少倍。您可以在您的代码里做一些修改,看看在您的实际情况下有多大影响。
3:kepler的shared memory上的原子操作一样是展开为多条指令实现的(前文其实也提到过),细节和fermi略有不同。
4:此外,global memory上的原子操作是一条指令的硬件实现,fermi上是,kepler上也是。(据说kepler还有进化)前文中也曾多次建议您考虑下,如果非要用原子操作,是否可以用global memory的原子操作,这个比shared memory上的原子操作更快。(不过您似乎一直无视此建议)
大致如上,祝您修改代码顺利!
1.我的原子访问不是合并的。
但是可以想办法映射为BANK FREE。也就是最适合的是共享存储器。
2.共享存储器是临时资源。
如果在GLOBAL上做的话,我还需要事先考虑这个空间如何申请,如何释放,非常非常麻烦。
因为核函数里面的_shared_变量似乎就是映射成共享存储器,不允许映射为GLOBAL。
但如果我将所有BLOCK每个分配一个GLOBAL对应的内存的话,这个数字就太太大了啊,不好做。
shared memory上不需要“合并”。
既然楼主有如此多的考虑,那么我觉得直接在shared memory上挺好。你觉得呢?
LZ您好,既然经您考虑和评估,无论是改写为规约还是使用global memory上的原子操作都不可行,那只有继续使用shared memory上的原子操作了。
同时也只有向NV反映这个需求,争取在下一代的maxwell核心的GPU上加强shared memory上的原子操作能力,如能成真,则再无纠结。
祝您元宵佳节愉快!
LZ您好,既然经您考虑和评估,无论是改写为规约还是使用global memory上的原子操作都不可行,那只有继续使用shared memory上的原子操作了。
同时也只有向NV反映这个需求,争取在下一代的maxwell核心的GPU上加强shared memory上的原子操作能力,如能成真,则再无纠结。
祝您元宵佳节愉快!
对了,OPENCL似乎就可以声明一个共享空间,映射为全局内存的。
CUDA为什么就没有这个功能呢?