在新版5.0的手册中,F.4.3章节中关于以64-bit宽度访问shared memory的说明中,举了一个例子:extern shared float shared ;
double data = shared[BaseIndex + tid];
我的问题是:
比如,对于tid0,它读取的应该是bank0和bank1。那么tid1读取的是bank1和bank2吧? 不会是bank2和bank3吧?
对于这个例子,因为double是64bit宽,每次读取shared时读两个bank。因为每个tid在同一个circle读取的是不同的bank,所以没有conflict。这样理解对么?
另外对于F.4.3第二段的第一句话:A shared memory request for a warp does not generate a bank conflict between two threads that access any address within the same 32-bit word (even though the two addresses fall in the same bank )
我个人理解的是:即使一个warp中的任意两个thread同时访问同一个bank中的不同字(此处错误,应该为字节(感谢横扫千军版主)) 的地址,也不会发生冲突。
不知道这样理解对不对。
求各位高手出招啊
楼主你好。
手册有大量错误存在,而且存在好久都无人修正。今日你看到的就是一个。
这个例子实际上是错误的,因为:
extern shared float shared ;
double data = shared[BaseIndex + tid]; //在这里会导致一个普通的LDS访问,而不是LDS.64
因为你的数组被定义为float , 而不是double , 实际上编译器会执行一次32位存取,然后cast这个32bit的float值为64bit的double, (通过fermi的F2F指令,当然,你可以不关心这个)。
因为这个例子是错误的,他执行的自然总是32位读取,而不是64位。因为你假设的前提(64位读取)实际上在此例子中并不存在,所以你的结论自然无需讨论。
对于您的后一个个问题,您的确可以理解为在一shared memory bank为4B宽的fermi硬件上,硬件有能力将1个4B值读取,然后分散给多个线程(中的寄存器),以及将来自多个线程的值,组合为1个4B, 然后写入。
您的这个理解是正确的,但说法需要商榷:“我个人理解的是:即使一个warp中的任意两个thread同时访问同一个bank中的不同字的地址,也不会发生冲突。 不知道这样理解对不对。”
改为: 不同字节(而不是不同字,字(word)有其他含义)。即可正确。
EDIT,更新于原帖发表16个小时后:
最后一段是接楼主的原文中的英文部分说的,这里暗含了“这些bytes在1个4B范围内的“含义,而不是指任意的bytes。请其他应用者不要断章义。
此外,如果对楼主你的例子修改一下,改成:
extern shared double shared ;
double data = shared[BaseIndex + tid];
那么这个访问,根据手册的说法,的确是无冲突的。但具体的原因我真的不能知道,因为NV没说。可能是硬件上的某种设计导致了这种情况。(例如民间消息说,有32个真实的banks, 但只有16组元素宽度的LSU(Load Store Unit), 再加上LSU的奇特的运行频率(据说一部分电路是倍频的,一部分运行于基频),以及LSU对存取操作的组合/拆分等因素,一起才导致的)(此来源信息完全不可靠,仅供参考)。
建议楼主记住结论(的确无bank conflict的,不仅仅是手册说,实际应用中也如此)。同时建议其他版主给出原厂的解释。
这是对fermi的阐述。因为你刚才询问的手册章节也是谈论fermi的。
我实在不知道该说什么了,感觉就像是攒了一个月生活费想去买辆自行车,结果店家直接送了一辆法拉利!
BZ你太牛逼了!!!
才看了一遍,继续第二遍。
太激动了
万能的BZ,我又看了一遍你修改过的例子,又有点小疑惑。
比如,
把shared定义为double,那么按照手册上“Each bank has a bandwidth of 32 bits per two clock cycles ”的说法,那么shared的每个元素是不是都需要4个circle才能完成存取呢?
如果是double类型的话,每个warp里32个线程就需要64个bank (因为一个bank只占了半个double),那么bank0-31和bank32-63是重合的。如果这时候写入的话,bank里的数据就成了随机数了吧?
另外,NV提供的linux下的debug工具可以查看每次运算执行了几次circle么?
我不光是cuda菜鸟,C语言其实也是才入门,也没学过数据结构,希望BZ不要觉得烦啊
(1)在2.x上,有2种频率,SM运行在较低的频率上,而SP和其他的一些单元运行在倍频上。
Shared memory也运行在基本频率上,在这个基本频率上,它的32个banks, 可以在一个时钟内,给出32个4B数据。也就是128B/cycle. 但是如果算到SP频率上,就只有64B/cycle。
那么回到你的问题,如果是32个线程,每个8B, 是需要256B的总数据量的,那么自然是需要4个时钟周期的(请注意这个周期针对是SP的那种倍频说的,如果算基本频率,那么需要2个)。
(2)这32个线程的double, 来自于32个banks. 每个banks连续提供了2次数据。你的描述以及后续的问法,表明你对这个概念不理解,我举个简单的比喻,你可以理解成32挺机枪(banks就是那黑洞洞的机枪口),每次大家发出1发子弹(4B),double就相当于双发的子弹(8B), 依然还是这32挺机枪,只不过每个洞口(bank)里连续2次出来而已(假设此机枪支持连续出来2次)。 因为我重新对你解释了概念,所以你原来的概念已经发生了改变,不再存在。因为你原来的概念不再存在,所以基于此概念的问题,以及它的解释也不再需要存在。
(3)我不懂Linux(R), 也无法为此提供建议。我建议你咨询其他会员和版主。
对了,你的cycle拼写有误,你拼成了circle。建议更正。
再次感谢!
第一个问题我明白了,但是对于第二个问题,我还是问题想问一下。
您在第二个问题的回复中说到了”发射两次“,这也让我想到了手册上介绍64bit宽度读取时所用的half-warp一词,所以我也明白了。 但是仔细一想,bank应该是一个一维的存储空间,那么如果一个bank连续地读两次,那么这个double数的高低位不就重复了么?如果连续写两次,那么写入的32bit就被覆盖掉了啊。
还烦请BZ帮忙解惑啊。 顺便问一下,BZ您都是从哪儿学到的这些呢?有什么推荐的教材么? 十分感谢!
我看了下CUDA4.2的F4.3,说了如果warp中的两个线程访问到相同32bit中的某个byte时,是不会有bank conflict,会被一次广播操作完成。而如果访问的是不同32bit并且bank相同,则会有冲突产生。
64bit访问的时候是以half-warp,因此也不会产生bank conflict
这里的“发射2次子弹”是为了你能详细的理解,对应的,是一个bank连续给出归属于这个bank的2个32-bit数据。
“bank应该是一个一维的存储空间,那么如果一个bank连续地读两次,那么这个double数的高低位不就重复了么?”
–说下这个,1维,与,只有1个存储空间,是不同的概念。我们常见的int dog 也是一维的,但里面不是只有1个元素的存储空间。与之对应,banks是能存放一定地址规律的元素(其实这个是地址转换后的结果),而不是只能存放一个元素。
说下32个banks, 用存放float/int之类大小为4B的元素为例,有32个banks(假设编号是从1开始的), 以及你有地址上连续的100个元素(依然假设编号从1开始,为了方便理解),如果1号元素在bank 1, 那么 11号元素就在bank 11, 那么22号元素就在bank 22, 32号元素就在bank 32。然后再有地址上继续的,就转圈回来了,33号元素也在bank 1。
正因为如此,如果你的32个线程同时访问的32个int/float都是正好来自这32个banks, 那么这32个banks都在忙碌,你也一次能得到这一批元素;但是,如果你32个线程,需要的元素来自31个banks,
那么必然有1个bank是空闲的,而另外一个bank则要吐出2个元素给你(不考虑特殊情况),这样就降低了效率。因为要等待这一批元素整体的读出,需要第一次使用31个bank(剩1个空闲), 第二次则前31个干过活动banks中的1个bank, 继续再干一次。这样就导致了需要2次。这样做,就叫冲突的访问,因为冲突导致有的bank有时不能工作,以及有的bank有时需要多次工作,所以才是效率降低的原因。
因为重新对你阐述了你脑海中的“一维的存放空间和一个存储空间”的区别,所以你的概念发生了变化。因为你的前置概念发生了变化,所以你的问题的前提不再存在,所以问题也不再存在。因为问题不再存在,所以也无需回答。
非常感谢!我对您的回复的理解是:
bank的最小操作单位是byte,而不是word;
如果两个thread同时盯 …
(1)不是bank的最小单位是byte, 而是任何你的卡任何最小访存单位都是byte(当然,有支持小于byte寻址能力的CPU存在。例如8051)。
(2)这里楼主你又糊涂了,上文你还是明白的。同一个word(4B)里的不同byte,被同一个warp里的多个线程使用,是没有bank conflict的。(是的。你前文自己还这么说过!)
建议浣熊每次洗完食物再吃。
额,我二了。。。
可是总版说“如果访问的是不同32bit并且bank相同,则会有冲突产生”,该如何理解这句话呢?
这句话和上贴无矛盾,他说的是,如果warp里多个线程要用到多个不同的4B, 需要从一个bank里出来的,那么则会冲突。而不是说,同一个4B里的不同bytes, 会冲突。请仔细注意识别!
其实是一个简单的事实:
1:多个线程访问同一个bank里面的4B长度的类型,如int,float,会发生一次广播,并无bank conflict。
2:多个线程访问存于同一个bank里面的1B长度的类型(一共有4个1B的类型,比如char),无论是同一个该1B类型,还是不同的该1B类型,都不会发生bank conflict,应该也是广播实现的。
第二种情况,从bank那里拿到,没有bank conflict,这个很好理解,因为只涉及一个bank,读一次就4B内容都拿到了。至于如何再转成所需的1B类型,这个我不清楚,看作是硬件提供的福利吧。
懂了! 这就是你之前说过的有的枪罢工了,有的枪就只好多出点力了。
感觉豁然开朗啊,哈哈哈。
这回没错了吧
我建议你针对整篇来评论,而不是节选。
当然,对于前一句“他说的是,如果warp里多个线程要用到多个不同的4B, 需要从一个bank里出来的,那么则会冲突”的评论还是没错的。
system
2013 年1 月 11 日 03:22
20
好吧,我给你个形象的表示
假设你的shared memory里面有个数组int array[64],那么bank的划分是
array[0] array[1] array[2] … array[31] array[32] …
bank0 bank1 bank2 … bank31 bank0 …
现在假设你一个warp里面的线程thread0和thread15, 访问array[0]那么不会有bank conflict,array[0]中的数据会以广播的方式给thread0和thread15
但是,如果你的thread0访问array[0]而thread15要访问array[32],那么就会出现bank conflict,因为它们都需要通过bank0来访问共享内存