全局内存中阵列上的CUDA原子操作

全局内存中阵列上的CUDA原子操作,cuda,race-condition,gpu-atomics,Cuda,Race Condition,Gpu Atomics,我有一个CUDA程序,它的内核基本上执行以下操作 我提供了笛卡尔坐标系中n个点的列表,例如尺寸为dim_x*dim_y的平面中的(x_I,y_I)。我相应地调用内核 对于这个平面上的每一点(x_p,y_p),我用公式计算出这n个点到达那里所需的时间;给定n个点以一定的速度移动 我将这些时间按递增顺序t_0,t_1,…t_n排序,其中t_I的精度设置为1。i、 e.如果t〃u i=2.3453,那么我只会使用t〃u i=2.3 假设时间是从正态分布生成的,我模拟3个最快的时间,以找出这3个点最早

我有一个CUDA程序,它的内核基本上执行以下操作

  • 我提供了笛卡尔坐标系中n个点的列表,例如尺寸为dim_x*dim_y的平面中的(x_I,y_I)。我相应地调用内核
  • 对于这个平面上的每一点(x_p,y_p),我用公式计算出这n个点到达那里所需的时间;给定n个点以一定的速度移动
  • 我将这些时间按递增顺序t_0,t_1,…t_n排序,其中t_I的精度设置为1。i、 e.如果t〃u i=2.3453,那么我只会使用t〃u i=2.3
  • 假设时间是从正态分布生成的,我模拟3个最快的时间,以找出这3个点最早到达的时间百分比。因此,通过随机实验假设prob_0=0.76、prob_1=0.20和prob_2=0.04。由于t_0在三个索引中排名第一,因此我还返回该点的原始索引(在对时间进行排序之前)。假设idx_0=5(一个整数)
  • 因此,对于这个平面上的每个点,我得到一对(prob,idx)
假设这些点中有n/2是一种,其余的是另一种。生成的示例图像如下所示

特别是当时间精度设置为1时,我注意到唯一的3个时间元组(t_0,t_1,t_2)的数量仅为总数据点的2.5%,即平面上的点数量。这意味着大多数时候,内核都是无用的模拟,而它只能使用以前模拟的值。因此,我可以使用一个字典,它的键作为时间的3元组,值作为索引和prob。因为据我所知和测试,STL不能在内核中访问,所以我构建了一个大小为201000000的浮点数组。这一选择是通过实验进行的,因为前3次没有一次超过20秒。因此t_0可以取{0.0,0.1,0.2,…,20.0}中的任何值,因此有201个选择。我可以为这样的字典构造一个键,如下所示

__global__ my_kernel(float* points,float* dict,float *p,float *i,size_t w,...){
  unsigned int col = blockIdx.y*blockDim.y + threadIdx.y;
  unsigned int row = blockIdx.x*blockDim.x + threadIdx.x;
  //Calculate time taken for each of the points to reach a particular point on the plane
  //Order the times in increasing order t_0,t_1,...,t_n
  //Calculate Key = t_o * 10^6 + t_1 * 10^3 + t_2
  if(dict[key]>0.0){
    prob=dict[key]-floor(dict[key])
    idx = floor(dict[key])
  }
  else{
    //Simulate and find prob and idx

    dict[key]=(prob+idx)
  }
  p[row*width+col]=prob;
  i[row*width+col]=idx;
} 
  • Key=t_o*10^6+t_1*10^3+t_2

就值而言,我可以将其设为(prob+idx)。由于idx是一个整数,并且0.0,除非在
else
分支中的模拟非常长(~100秒的浮点运算),否则全局内存中的查找表可能比运行计算慢。全局内存访问非常昂贵

在任何情况下,都无法通过使用条件分支“跳过工作”来节省时间。GPU的单指令多线程体系结构意味着分支两侧的指令将串行执行,除非块中的所有线程都遵循相同的分支

编辑: 由于引入了条件分支,您看到了性能的提高,并且没有任何死锁问题,这表明每个块中的所有线程总是采用相同的分支。我怀疑一旦
dict
开始填充,性能的提高就会消失

也许我误解了什么,但是如果你想计算一个事件的概率
x
,假设一个正态分布,并且给定平均值
mu
和标准偏差
sigma
,就不需要生成随机数负载和近似高斯曲线。您可以直接计算概率:

p = exp(-((x - mu) * (x - mu) / (2.0f * sigma * sigma))) / 
    (sigma * sqrt(2.0f * M_PI));

由于
dict
在全局内存中,我认为您的
syncthreads
没有做任何有用的事情。这只是块级同步。这也是有风险的,因为有可能一个块的某些成员会调用它,而其他成员不会调用它。我已经在删除syncthreads的情况下尝试过了。输出是一样的。从我对你的文章的肤浅阅读中了解到,你不一定在
和2D线程索引之间有一对一的映射。如果这是真的,我同意你很可能有一个竞态条件,因为所有线程都将读取
dict[key]
,但其中一些线程(可能是那些
dict[key]==0的线程,因为我不期望
dict
会有负值)正在改变它们。@SandipanBhattacharyya在这种情况下,块中的所有线程都采用相同的分支。如果一些人使用
If
而另一些人使用
else
,则会出现死锁。在任何情况下,运行模拟的某些线程和读取dict的某些线程都没有任何优势,因为块中的所有线程都必须彼此等待。请提供再现问题的代码的最小版本,好吗?我恐怕这个问题将一直无法回答。第二张错误图片背后的问题可能也在未显示部分。谢谢您的回复!虽然我必须在这里补充几点。else分支实际上很长。我从{0,1,2}中I的正态分布生成500个随机数,其均值=t_I,标准偏差=函数(t_I),并进行计算以计算概率。另外,在第一种情况下,我的问题在10560 x 6800点的飞机上花费的时间是2.48秒。当我分配这么多的全局内存时,生成这张图像所花费的时间会跃升到3.19秒。但一旦我引入if-else分支,所花费的时间就会减少到2.39秒。这里的想法是不生成一帧输出,而是在多个帧中作为输入点给出,并在多个帧中作为输出概率返回,同时节省模拟所需的时间。这是我希望的,因为与单个帧中的总点数相比,唯一的3元组的数量要少得多,所以当同时使用2-3帧时,情况会更糟。因此,我已从问题中删除了syncthreads!谢谢你的解释,谢谢!现在我知道这不是CUDA的问题了。而且我可能无法质疑prope