Cuda原子变化标志

Cuda原子变化标志,cuda,atomic,Cuda,Atomic,我有一段串行代码,它是这样做的 if( ! variable ) { do some initialization here variable = true; } 我知道这在序列中工作得很好,只会执行一次。在CUDA中,什么原子操作才是正确的操作?在我看来,您想要的是代码中的“关键部分”。临界段允许一个线程执行一系列指令,同时阻止任何其他线程或线程块执行这些指令 例如,可以使用临界段来控制对内存区域的访问,以便允许单个线程对该区域进行无冲突的访问 原子本身只能用于一个非常有限的、基本

我有一段串行代码,它是这样做的

if( ! variable )
{
  do some initialization here 
  variable = true;
}

我知道这在序列中工作得很好,只会执行一次。在CUDA中,什么原子操作才是正确的操作?

在我看来,您想要的是代码中的“关键部分”。临界段允许一个线程执行一系列指令,同时阻止任何其他线程或线程块执行这些指令

例如,可以使用临界段来控制对内存区域的访问,以便允许单个线程对该区域进行无冲突的访问

原子本身只能用于一个非常有限的、基本上是单一的、单一变量的操作。但原子学可以用来建立一个关键部分

您应该在内核中使用以下代码来控制对关键部分的线程访问:

__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();
  }
我已经成功地测试并使用了上述代码。请注意,它本质上使用每个threadblock中的线程0作为请求程序在threadblock之间进行仲裁。如果您希望在获胜的threadblock中只有一个线程执行关键部分代码,则应进一步调整(例如,
if(threadIdx.x<…)
)您的关键部分代码

在一个信号量的warp仲裁中有多个线程会带来额外的复杂性,所以我不推荐这种方法。相反,让每个threadblock进行仲裁,如我在这里所示,然后使用普通threadblock通信/同步方法(例如
\uu syncthreads()
、共享内存等)控制您在获胜threadblock中的行为

请注意,这种方法将对性能造成代价。只有当您无法找出如何并行化算法时,才应该使用关键部分


最后,一句警告。与任何线程化并行体系结构一样,关键部分的不当使用可能导致死锁。特别是,假设threadblock和/或threadblock内扭曲的执行顺序是有缺陷的方法

因此,多个线程不会同时尝试修改同一个变量,这将导致未定义的行为。sgar91,是的,更正这是旧的遗留代码,我无法在结构上更改。所以基本上,第一个被执行的线程应该执行它,为warp中的其他线程阻塞它,并将变量更改为true,这样其他线程就不会进入该部分了。你所描述的对我来说并不是一个原子函数,而是一个关键部分。您可以在右上角的“cuda临界区”中搜索一些想法。不幸的是,我的一篇关于cuda评论部分的帖子被删除了。如果你喜欢,我可以把它作为答案贴在这里。“原子”函数只允许对“执行一些初始化”区域中的单个变量进行有限的操作。因此,如果涉及到这一领域,它可能无法用原子来服务,尽管原子学有助于构建关键部分。嗨,罗伯特,你的帖子会很受欢迎。我不明白这里的
\u syncthreads
的目的。它会迫使另一个线程到达这一点吗?因为在我看来,它们似乎是无用的,因为其他线程在所有情况下都会达到这一点,这里关键部分协商背后的一个想法是,您可能在threadblock中使用多个线程来执行关键部分“工作”。在这种情况下,在主线程正确协商并获得全局锁之前,他们不应该开始“工作”。
\uuu syncthreads()
将强制执行该行为,它们还将强制所有线程在释放锁之前完成关键部分“工作”。如果您在threadblock中不需要这种协作行为,那么您可能不需要
\uu syncthreads()
。以及为什么将信号量声明为
volatile
?正确操作可能不需要它。锁释放代码应转换为使用
volatile
直接写入内存,但
\uuu threadfence()
具有类似的用途。如果您确实有任何其他代码出于某种原因读取信号量,那么
volatile
将非常有用。@RobertCrovella根据CUDA文档
\uu threadfence()
只能用于确保正确的内存访问顺序。因此,如果在解锁后执行
\u threadfence()
,则线程可能处于介于
lock=0
\u threadfence
之间的中间状态。如果此时另一个线程获得了锁,那么前一个线程所做的一些内存写入操作对于后一个线程仍然是不可见的,并且出现在关键部分的中间。如果您严格遵守CUDA文档,则应将
\uuu threadfence
放在
lock=0
之前,否则不能保证任何有用的内容。