CUDA中的原子Saxpy

CUDA中的原子Saxpy,cuda,mutex,atomic,Cuda,Mutex,Atomic,我在CUDA有以下问题 假设我们有一个索引列表,其中一些或所有索引可以多次出现: inds = [1, 1, 1, 2, 2, 3, 4] 有了这些索引,我想在浮点数组x上执行原子saxpy操作(并行)。我不担心操作的应用顺序。也就是说,我想这样做,对于浮动a和k: x[i] = x[i]*a + k; 如果inds中没有重复的索引,这将是微不足道的 我目前的解决方案(不起作用)是: 我的机器总是死锁。即使对于非常简单的输入,如上面的示例数据 是否可以重写此函数以使其正常工作 示例答案 假设

我在CUDA有以下问题

假设我们有一个索引列表,其中一些或所有索引可以多次出现:

inds = [1, 1, 1, 2, 2, 3, 4]
有了这些索引,我想在浮点数组
x
上执行原子saxpy操作(并行)。我不担心操作的应用顺序。也就是说,我想这样做,对于浮动
a
k

x[i] = x[i]*a + k;
如果
inds
中没有重复的索引,这将是微不足道的

我目前的解决方案(不起作用)是:

我的机器总是死锁。即使对于非常简单的输入,如上面的示例数据

是否可以重写此函数以使其正常工作

示例答案 假设
k=0.1
a=0.95
,所有指数的初始值
args
0.5
,结果应该是:

[0.5, 0.7139374999999998, 
 0.6462499999999999, 0.575, 0.575, ...]
我使用Python计算了这些值,它们在CUDA中可能看起来不同。这是一个关于算法应该如何运行的示例,而不是一个好的样本集来处理竞争条件问题

参考文献 下面是一个线程,在该线程中,他们使用
atomicExch
实现
atomicAdd
(此时已存在浮点数):

一个例子如下所示:

__device__ inline void atomicAdd(float* address, float value) {
  float old = value;  
  float new_old;

  do {
    new_old = atomicExch(address, 0.0f);
    new_old += old;
  }
  while ((old = atomicExch(address, new_old)) != 0.0f);
};
这似乎有点容易,我不太明白如何适应它

其他解决方案 能够以这种方式解决这个问题,对于我以后遇到的与内存IO相关的问题有几个好处。因此,我想知道这是否可能


一种可能的不同方法是,计算每个索引在CPU上出现的次数,然后在此之后在GPU上执行“常规”saxpy。我假设还有其他的可能性,但我仍然对这个问题的答案感兴趣

如果这是一个非并行问题,您只需执行以下操作:

*adr = *adr * a + k;
由于有多个线程在
adr
上运行,因此我们应该使用原子操作进行读写

float adrValue = atomicExch(adr, -1.0f)
float newValue = adrValue * a + k
atomicExch(adr, newValue)
但是,我们必须注意另一个线程在读取步骤(ln1)和写入步骤(ln3)之间更新了
adr

所以我们的三步操作是非原子的

为了使其原子化,我们应该使用比较和交换(atomicCAS)来确保我们只在从内存读取后其值保持不变的情况下更新内存。我们可以简单地重复我们的步骤,在每次迭代中使用
adr
中的当前值作为计算输入,直到步骤3返回预期的锁值
-1.0f

do {
    float adrValue = atomicExch(adr, -1.0f)
    float newValue = adrValue * a + k
    adrValue = __int_to_float(atomicCAS(adr, 
                                        __float_as_int(-1.0f),
                                        __float_as_int(newValue)))
} while (adrValue != -1.0f)

PS:考虑上面的伪代码

,把这个当作前缀SUMWON,你可以更好地处理这个问题,你用你所展示的示例输入的预期答案来更新这个问题吗?@ TalnMyes看到更新的问题。如果不知道每个索引在前面的数组中出现的次数,我就看不出如何做到这一点。你能详细说明一下你的想法吗?我也试过了,但我得到的结果似乎和我第一次建议的解决方案非常相似。也就是说,它有时有效,但并不总是有效。
float adrValue = atomicExch(adr, -1.0f)
float newValue = adrValue * a + k
atomicExch(adr, newValue)
do {
    float adrValue = atomicExch(adr, -1.0f)
    float newValue = adrValue * a + k
    adrValue = __int_to_float(atomicCAS(adr, 
                                        __float_as_int(-1.0f),
                                        __float_as_int(newValue)))
} while (adrValue != -1.0f)