我有一段串行代码,它执行的操作类似于这样:
if( ! variable )
{
do some initialization here
variable = true;
}
我理解在串行中这个操作可以完美地执行一次。在CUDA中,正确的原子操作是什么?
if( ! variable )
{
do some initialization here
variable = true;
}
我理解在串行中这个操作可以完美地执行一次。在CUDA中,正确的原子操作是什么?
在我看来,您需要的是代码中的“关键区域(critical section)”。 关键区域允许一个线程执行一系列指令,同时防止任何其他线程或线程块执行这些指令。
例如,可以使用关键区域控制对内存区域的访问,以便单个线程无冲突地访问该区域。
原子操作本身只能用于非常有限的、基本上是单个变量的操作。但是可以使用原子操作来构建关键区域。
您应该在内核中使用以下代码来控制线程对关键区域的访问:
__syncthreads();
if (threadIdx.x == 0)
acquire_semaphore(&sem);
__syncthreads();
//begin critical section
// ... your critical section code goes here
//end critical section
__threadfence(); // not strictly necessary for the lock, but to make any global updates in the critical section visible to other threads in the grid
__syncthreads();
if (threadIdx.x == 0)
release_semaphore(&sem);
__syncthreads();
在定义内核之前,请定义这些辅助函数和设备变量:
__device__ volatile int sem = 0;
__device__ void acquire_semaphore(volatile int *lock){
while (atomicCAS((int *)lock, 0, 1) != 0);
}
__device__ void release_semaphore(volatile int *lock){
*lock = 0;
__threadfence();
}
我已经测试并成功使用了上面的代码。注意,它基本上是通过在每个线程块中使用线程0作为请求者来仲裁线程块。如果您只想让获胜的线程块中的一个线程执行关键部分代码,那么您应该进一步确定(例如,if (threadIdx.x < ...)
)您的关键部分代码。
让warp内的多个线程互相竞争信号量会带来额外的复杂性,因此我不建议采用这种方法。相反,像我在这里展示的那样,让每个线程块进行仲裁,然后使用普通的线程块通信/同步方法(如__syncthreads()
、共享内存等)控制在获胜的线程块中的行为。
请注意,这种方法将对性能产生显著影响。仅当无法找到其他并行算法时才应使用关键部分。
最后,警告一句。与任何线程并行架构一样,不当使用关键部分可能导致死锁。特别地,假设线程块和/或warp内的执行顺序是一种有缺陷的方法。
这里是使用binary_semaphore
实现单个设备全局“锁定”的示例,可用于访问控制到关键部分。
__syncthreads
的目的。它是否强制其他线程到达此点?因为在我看来,它们似乎是无用的,因为其他线程在所有情况下都会到达此点,不是吗? - Antoine Morrier__syncthreads()
将强制执行该行为,并且它们还将强制要求在释放锁之前,所有线程都已完成关键部分的“工作”。如果您在线程块中不需要这种协作行为,则可能不需要__syncthreads()
。 - Robert Crovellavolatile
? - Antoine Morriervolatile
直接写入内存,但__threadfence()
也有类似的作用。如果您有任何其他代码因某种原因读取信号量,则volatile
将非常有用。 - Robert Crovella__threadfence()
仅用于确保内存访问的正确顺序。因此,如果您在解锁后使用__threadfence()
,则可能会导致线程处于中间状态,即在“ lock = 0”和__threadfence
之间。 如果另一个线程此时获取锁,则前一个线程进行的某些内存写入仍然可能对后一个线程不可见,并出现在关键部分的中间位置。如果您严格遵循CUDA文档,应该将__threadfence
放置在lock = 0
之前,否则它无法保证任何有用的东西。 - user8044236