我有以下算法:
__global__ void Update(int N, double* x, double* y, int* z, double* out)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < N)
{
x[i] += y[i];
if (y[i] >= 0.)
out[z[i]] += x[i];
else
out[z[i]] -= x[i];
}
}
重要的是要注意out小于x。假设x,y和z总是相同的大小,比如1000,并且out总是更小,比如100. z是x和y中的每一个对应的out中的索引。
除了更新外,这都是找到的。由于z不包含唯一值并且具有重复项,因此可能存在跨线程冲突。因此,我目前使用atomicAdd的原子版本实现了这一点,并使用compare和swap进行减法。这显然很昂贵,这意味着我的内核运行时间要长5到10倍。
我想减少这个但是我能想到这样做的唯一方法是每个线程都有自己的out版本(可以是大型,10000 +,X 10000+线程)。这意味着我设置10000双[10000](可能在共享?)中调用我的内核,然后在这些数组之间求和,也许在另一个内核中。当然必须有更优雅的方式来做到这一点?
值得注意的是x,y,z和out驻留在全局内存中。由于我的内核(我有其他类似的东西)非常简单,我还没有决定将位复制到共享(内核上的nvvp显示相同的计算和内存,因此我认为在添加从中移动数据的开销时不会获得太多性能全球共享,再回来,任何想法?)。
答案 0 :(得分:2)
方法1:
构建一组&#34;交易&#34;。由于每个线程只有一个更新,因此您可以轻松构建固定大小的事务&#34;记录,每个线程一个条目。假设我有{8个线程(为了简化演示)和out
表中的一些任意数量的条目。让我们假设我的8个帖子想做8个这样的交易:
thread ID (i): 0 1 2 3 5 6 7
z[i]: 2 3 4 4 3 2 3
x[i]: 1.5 0.5 1.0 0.5 0.1 -0.2 -0.1
"transaction": 2,1.5 3,0.5 4,1.0 4,0.5 3,0.1 2,-0.2 3,-0.1
现在对交易执行sort_by_key,按z[i]
的顺序排列:
sorted: 2,1.5 2,-0.2 3,0.5 3,-0.1 3,0.1 4,1.0 4,0.5
现在对事务执行reduce_by_key操作:
keys: 2 3 4
values: 1.3 0.5 1.5
现在根据键更新out[i]
:
out[2] += 1.3
out[3] += 0.5
out[4] += 1.5
方法2:
正如您所说,全局内存中有数组x
,y
,z
和out
。如果你打算使用z
这是一个&#34;映射&#34;反复地,您可能希望按z
:
index (i): 0 1 2 3 4 5 6 7
z[i]: 2 8 4 8 3 1 4 4
x[i]: 0.2 0.4 0.3 0.1 -0.1 -0.4 0.0 1.0
分组由z [i]:
index (i): 0 1 2 3 4 5 6 7
z[i]: 1 2 3 4 4 4 8 8
x[i]:-0.4 0.2 -0.1 0.3 0.0 1.0 0.4 0.1
这个或它的一些变体将允许你消除必须在方法1中重复进行排序操作(同样,如果你使用相同的&#34;映射&#34;矢量重复)。