【问题标题】:Cuda atomic lock: threads in sequenceCuda原子锁:顺序线程
【发布时间】:2014-11-05 14:53:06
【问题描述】:

我有一段代码需要严格执行。我为那段代码使用了一个锁,以便内核的每个线程(每个块设置一个线程)原子地执行那段代码。线程的顺序让我感到困扰 - 我需要线程根据它们的索引(或者实际上,按照它们的 blockIdx 的顺序)按时间顺序执行,从 0 到 10(而不是随机的,例如 5、8、3, 0,...等)。有可能吗?

这是一个示例代码:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<math_functions.h>
#include<time.h>
#include<cuda.h>
#include<cuda_runtime.h>

// number of blocks
#define nob 10

struct Lock{
  int *mutex;
  Lock(void){
    int state = 0;
    cudaMalloc((void**) &mutex, sizeof(int));
    cudaMemcpy(mutex, &state, sizeof(int), cudaMemcpyHostToDevice);
  }
  ~Lock(void){
    cudaFree(mutex);
  }
  __device__ void lock(void){
    while(atomicCAS(mutex, 0, 1) != 0);
  }
  __device__ void unlock(void){
    atomicExch(mutex, 0);
  }
};


__global__ void theKernel(Lock myLock){
  int index = blockIdx.x; //using only one thread per block

  // execute some parallel code

  // critical section of code (thread with index=0 needs to start, followed by index=1, etc.)
  myLock.lock();

  printf("Thread with index=%i inside critical section now...\n", index);

  myLock.unlock();
}

int main(void)
{
  Lock myLock;
  theKernel<<<nob, 1>>>(myLock);
  return 0;
}

给出以下结果:

Thread with index=1 inside critical section now...
Thread with index=0 inside critical section now...                                                                                                                                   
Thread with index=5 inside critical section now...                                                                                                                                            
Thread with index=9 inside critical section now...
Thread with index=7 inside critical section now...
Thread with index=6 inside critical section now...
Thread with index=3 inside critical section now...
Thread with index=2 inside critical section now...
Thread with index=8 inside critical section now...
Thread with index=4 inside critical section now...

我希望这些索引从 0 开始并按时间顺序执行到 9。

我认为修改 Lock 以实现此目的的一种方法如下:

struct Lock{
  int *indexAllow;
  Lock(void){
    int startVal = 0;
    cudaMalloc((void**) &indexAllow, sizeof(int));
    cudaMemcpy(indexAllow, &startVal, sizeof(int), cudaMemcpyHostToDevice);
  }
  ~Lock(void){
    cudaFree(indexAllow);
  }
  __device__ void lock(int index){
    while(index!=*indexAllow);
  }
  __device__ void unlock(void){
    atomicAdd(indexAllow,1);
  }
};

然后通过将索引作为参数传递来初始化锁:

myLock.lock(index);

但这让我的电脑停了下来……我可能遗漏了一些明显的东西。

如果有人能提供帮助,我将不胜感激!

谢谢!!!

【问题讨论】:

  • stackoverflow.com/questions/21341495/cuda-mutex-and-atomiccas。我有时可以通过在 CPU 上运行上述部分来解决这个问题(来回复制很慢,但尽管我们所有 cuda 程序员都希望,CPU 对于某些操作来说要快得多)。其他解决方案取决于关键部分发生的情况。

标签: cuda gpu-atomics


【解决方案1】:

我稍微更改了您的代码。现在它会产生你想要的输出:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<math_functions.h>
#include<time.h>
#include<cuda.h>
#include<cuda_runtime.h>

// number of blocks
#define nob 10

struct Lock{
  int *mutex;
  Lock(void){
    int state = 0;
    cudaMalloc((void**) &mutex, sizeof(int));
    cudaMemcpy(mutex, &state, sizeof(int), cudaMemcpyHostToDevice);
  }
  ~Lock(void){
    cudaFree(mutex);
  }
  __device__ void lock(uint compare){
    while(atomicCAS(mutex, compare, 0xFFFFFFFF) != compare);    //0xFFFFFFFF is just a very large number. The point is no block index can be this big (currently).
  }
  __device__ void unlock(uint val){
    atomicExch(mutex, val+1);
  }
};


__global__ void theKernel(Lock myLock){
  int index = blockIdx.x; //using only one thread per block

  // execute some parallel code

  // critical section of code (thread with index=0 needs to start, followed by index=1, etc.)
  myLock.lock(index);
  printf("Thread with index=%i inside critical section now...\n", index);
  __threadfence_system();   // For the printf. I'm not sure __threadfence_system() can guarantee the order for calls to printf().
  myLock.unlock(index);
}

int main(void)
{
  Lock myLock;
  theKernel<<<nob, 1>>>(myLock);
  return 0;
}

lock() 函数接受compare 作为参数并检查它是否等于mutex 中的值。如果是,则将0xFFFFFFFF 放入mutex 中,表示锁是由线程获取的。因为mutex在构造函数中被初始化为0,所以只有块ID为0的线程才能成功获取锁。在unlock 中,我们将下一个块ID 索引放入mutex 以保证您想要的排序。此外,由于您在 CUDA 内核中使用了 printf(),我认为您需要调用 threadfence_system() 才能以相同的顺序在输出中查看它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-15
    • 1970-01-01
    • 2018-01-24
    • 2016-08-10
    • 1970-01-01
    相关资源
    最近更新 更多