所有问题都在这里。
我了解为什么在使用volatile
及其类似功能时需要变量为__threadfence_block
>
请注意,为了使此订购保证为真,请遵守 线程必须真正观察内存,而不是缓存版本; 这可以通过使用Volatile中详细介绍的volatile关键字来确保 预选赛。
但是我想知道为什么在使用volatile
函数时我们不需要变量为__syncthreads
答案 0 :(得分:3)
根据the programming guide,__syncthreads()
既是执行障碍,又是内存屏障:
等待直到线程块中的所有线程都达到此点为止,并且这些线程在
__syncthreads()
之前进行的所有全局和共享内存访问对于该块中的所有线程可见。 / p>
内存隔离功能(即“可见性”)“强制”对共享和全局内存的所有更新对其他线程可见。
我想这就是你要问的。我不认为做出诸如“在使用__syncthreads()
时不需要使用volatile”这样的笼统声明是明智的。这将取决于代码。但是在某些情况下,例如classical parallel reduction,在块级缩减的每个步骤中使用__syncthreads()
将意味着用于这种缩减的共享内存不必标记为{{1} }。
由于volatile
既是执行障碍,又是内存屏障,所以对于__syncthreads()
的使用,我们可以做出某些陈述,这些陈述仅适用于__syncthreads()
的使用。 / p>
假设我有以下代码:
__threadfence()
在这种情况下,保证执行if语句的特定块中的任何线程都将__global__ void k(int *data){
...
*data = 1;
__syncthreads();
if (*data == 1){
...}
...
}
视为1。这有两个组成部分:
*data
是(设备范围内的)内存围墙。它强制所有写入该值的线程使该值可见。这有效地意味着,因为这是设备范围的内存屏障,所以写入的值至少已填充了L2高速缓存(L2缓存是全局内存的设备范围的中介程序,实际上是全局内存的代理)。
__syncthreads()
是(整个线程块)执行障碍。它会强制所有线程到达障碍处,然后再继续进行操作。这种执行排序行为意味着,当任何线程执行上述if语句时,以上第1项中的保证即生效。
请注意,这里有一个细微的区别。在其他块中,在代码的其他位置上的其他线程,可能会看到也可能看不到其他块所写的值。
只有将执行同步和内存防护结合在一起时,才能确定一个线程填充的值对另一个线程是真正可见的。而且,由于不使用协作组,CUDA没有提供跨单独的块同步执行的机制。
__syncthreads()
本身会使值最终可见,但是如果不了解写入线程和读取线程之间的相对执行顺序,就不可能仅基于代码检查。
与__threadfence()
类似,volatile
保证与__threadfence()
类似(对于写线程),但也有所不同。 __threadfence()
确保写入线程最终会将其数据推送到L2(即使其可见)。 volatile
做类似的事情,但也保证读取线程将不会在L1中读取“过时的副本”,但会在至少一次读取L2中的值时进入L2(至少)以获取当前值。代码。
请注意,永远不会因另一个SM上的设备代码活动而触发的L1缓存数据“无效”。 volatile
有效地保证了负载将绕过L1。 volatile
还保证商店将直接转到L2。 __threadfence()
的行为与后者类似(至少从线程前进到__threadfence()
之后),但不保证其他SM中的L1状态或其他SM中的线程将如何读取值。