我在CUDA中遇到以下问题。
假设我们有一个索引列表,其中一些或所有索引可以出现多次:
inds = [1, 1, 1, 2, 2, 3, 4]
使用这些索引,我想在float数组x
上执行原子saxpy操作(并行)。我并不担心操作的顺序。也就是说,我想这样做,对于花车a
和k
:
x[i] = x[i]*a + k;
如果inds
中没有重复的索引,这将是微不足道的。
我目前的解决方案(不起作用)是:
// assume all values in adr are greater than or equal to 0.
// also assume a and k are strictly positive.
__device__ inline void atomicSaxpy(float *adr,
const float a,
const float k){
float old = atomicExch(adr, -1.0f); // first exchange
float new_;
if (old <= -1.0f){
new_ = -1.0f;
} else {
new_ = old*a + k;
}
while (true) {
old = atomicExch(adr, new_); // second exchange
if (old <= -1.0f){
break;
}
new_ = old*a + k;
}
}
在许多情况下,这似乎会返回正确的答案。
当你没有得到正确的答案时,我认为这是我的想法:
old
在第一次交易中获得-1.0f
的值。 =&GT; new_ = -1.0f
old
也会在第二次交换中获得-1.0f
的值。有一种不同的方法是:
__device__ inline void atomicSaxpy(float *adr,
const float ia,
const float k){
float val;
while (true) {
val = atomicExch(adr, -1.0f);
if (val > 1.0f){
break;
}
atomicExch(adr, val*ia + k);
}
}
我的机器上一直死锁。即使是非常简单的输入,例如上面的示例数据。
是否可以重写此功能以使其正常运行?
假设所有索引的k=0.1
和a=0.95
以及args
的初始值为0.5
,结果应为:
[0.5, 0.7139374999999998,
0.6462499999999999, 0.575, 0.575, ...]
我使用Python计算了这些值,它们在CUDA中可能看起来不同。这是算法应该如何表现的一个例子,而不是一个很好的样本集遇到竞争条件问题。
这是一个使用atomicAdd
实现atomicExch
(此时已存在浮点数)的线程:
https://devtalk.nvidia.com/default/topic/458062/atomicadd-float-float-atomicmul-float-float-/
示例如下:
__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上出现的次数,然后执行&#34;常规&#34;之后GPU上的saxpy。我假设还有其他可能性,但我仍然对这个特定问题的答案感兴趣。
答案 0 :(得分:1)
如果这是一个非并行问题,您只需执行此操作:
*adr = *adr * a + k;
由于在adr
上运行多个线程,我们应该使用原子操作进行读写。
float adrValue = atomicExch(adr, -1.0f)
float newValue = adrValue * a + k
atomicExch(adr, newValue)
但是,我们必须意识到另一个线程在我们的阅读步骤(ln1)和我们的写入步骤(ln3)之间更新adr
的可能性。
所以我们这里的三步操作是非原子的。
为了使它成为原子,我们应该使用compare-and-swap(atomicCAS)来确保我们只有在我们从中读取它的值时才更新内存。我们可以在每次迭代中重复我们的步骤,使用adr
中的then-current值作为计算输入,直到step3返回预期的锁定值-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:考虑上面的伪代码