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

时间:2013-11-27 10:30:27

标签: cuda race-condition gpu-atomics

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

  • 我提供了笛卡尔坐标中n个点的列表,例如(x_i,y_i)在维度为dim_x * dim_y的平面中。我相应地调用内核。
  • 对于这个平面上的每个点(x_p,y_p),我用公式计算这n个点中的每个点到达那里所需的时间;鉴于这些n点以一定的速度移动。
  • 我按照递增顺序t_0,t_1,... t_n对这些时间进行排序,其中t_i的精度设置为1.即如果t'_i = 2.3453,那么我只使用t_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是一种,其余的是其他点。生成的示例图像如下所示。

Unoptimized

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

  • Key = t_o * 10 ^ 6 + t_1 * 10 ^ 3 + t_2

就值而言,我可以将其设为(prob + idx)。由于idx是一个整数且0.0< = prob< = 1.0,我可以稍后通过

检索这两个值
  • 概率=字典[键] -floor(字典[键])
  • idx = floor(dict [key])

所以现在我的内核看起来像下面的

__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;
} 

结果与大多数积分的原始程序非常相似,但对于某些人来说这是错误的。

Optimized but not correct

我很确定这是因为竞争条件。请注意,dict已使用全零进行初始化。基本思想是在dict的特定位置使数据结构“读多次写入”。

我知道可能有更多优化方法来解决这个问题而不是分配如此多的内存。那个案子请告诉我。但我真的很想了解为什么这个特定的解决方案失败了。特别是我想知道如何在此设置中使用atomicAdd。我没有使用它。

1 个答案:

答案 0 :(得分:0)

除非您在else分支中的模拟非常长(~100s的浮点运算),否则全局内存中的查找表可能比运行计算慢。全局内存访问非常昂贵!

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

编辑:

由于引入了条件分支而导致性能提升的事实,你没有遇到任何死锁问题,这表明每个块中的所有线程都在同一个分支。我怀疑,一旦dict开始填充,性能提升就会消失。

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

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