线程不会因内存访问而失速
来自Vasily Volkov的着名论文http://www.cs.berkeley.edu/~volkov/volkov10-GTC.pdf
我假设基于这个陈述:
__device__ int a;
int b, c, d;
a = b * c;
// Do some work that is independent of 'a'
// ...
d = a + 1;
比这快
__device__ int a;
int b, c, d;
a = b * c;
d = a + 1;
// Do some work that is independent of 'a'
// ...
我只是假设因为我在写入全局内存时给了线程执行不同指令的机会,而在第二种方法中我不是。
我的假设是正确的吗?
如果我的假设是正确的,那么在内核开头设置所有将在以后使用的变量是一个好习惯吗?鉴于它们彼此独立,还假设a
未被缓存。
答案 0 :(得分:2)
引用的档位确实是内存读取。
它指出内存读取不会产生停顿,使用读取的值,假设它不可用,会导致停顿。
假设我有:
__device__ int a[32];
然后这个线程代码不会导致停顿(虽然它会生成内存事务):
int b = a[0];
但如果我这样做,我会得到一个摊位:
int b = a[0];
int c = a[1];
int d = b * c; // stall occurs here
因此,如果我能做到这一点:
int b = a[0];
int c = a[1];
// do lots of other work here
int d = b * c; // this might not stall
对于Fermi和Kepler GPU,写入(并且从先前写入的值读取,假设它们没有从缓存中逐出)到全局存储器由缓存提供服务,因此线程代码似乎是写入全局内存通常是写入L1或L2缓存,而实际的全局内存写入事务将在以后发生,并不一定会造成任何类型的停顿。
因此,在您的示例中,通常a
将由缓存提供服务:
__device__ int a;
int b, c, d;
a = b * c; // a gets written to cache
d = a + 1; // a is serviced from cache
请注意,从缓存服务仍然比最快的访问机制(例如寄存器和共享内存)慢,但它比全局内存停顿要快得多。
说完这一切之后,编译器通常会做一些可能影响这个的事情。首先,编译器可能会发现独立的工作,而不是您手动重新排序代码,并在某种程度上为您重新排序代码。其次,在您的示例中,除了在某个时刻更新全局内存中的值之外,编译器还会发现a
被重用并且很可能将其分配给寄存器变量。它位于寄存器中这意味着在上面示例的最后一行使用a
很可能会从寄存器中获取服务,而不是全局内存或缓存。
所以回答你的问题,我会说,一般来说,你的假设是不正确的。编译器将发现a
的重用并将其分配给寄存器,完全消除您认为存在的危险。从理论上讲,如果没有缓存(对于计算1.x设备是正确的)并且没有寄存器,那么编译器可能会被迫按照您的建议使用全局内存,但实际上它不会发生。