我想与APARAPI并行实现一个熵函数。
在该功能中,我需要计算向量中的不同键,但无法正确执行。
假设我们只有3个不同的值。
这是我的代码:
final int[] V = new int[1024];
// Initialization for V values
final int[] count = new int[3];
Kernel kernel = new Kernel(){
@Override
public void run(){
int gid = getGlobalId();
count[V[gid]]++;
}
};
kernel.execute(Range.create(V.length));
kernel.dispose();
运行此代码段后,当我打印count []值时,它会给我1,1,1。
似乎
count[V[gid]]++
对于每个V [gid]仅执行1次。谢谢。
最佳答案
所以这就是问题所在。 ++运算符实际上是三个操作合而为一:读取当前值,对其进行递增,写入新值。在Aparapi中,您可能有1024个GPU线程同时运行。这意味着它们将读取该值,可能是在该值为0的同时读取该值,然后将其递增为1,然后所有1024个线程都将写入1。
您尝试执行的操作称为Map-reduce函数。您只是跳过了很多步骤。您需要记住,Aparapi是一个没有线程安全性的系统,因此您必须编写算法来适应这种情况。那就是Map-reduce出现的地方,这是一种方法。我刚刚写了它,并将其添加到Aparapi存储库的新家中,详细信息如下。
int size = 1024;
final int count = 3;
final int[] V = new int[size];
//lets fill in V randomly...
for (int i = 0; i < size; i++) {
//random number either 0, 1, or 2
V[i] = (int) (Math.random() * 3);
}
//this will hold our values between the phases.
int[][] totals = new int[count][size];
///////////////
// MAP PHASE //
///////////////
final int[][] kernelTotals = totals;
Kernel mapKernel = new Kernel() {
@Override
public void run() {
int gid = getGlobalId();
int value = V[gid];
for(int index = 0; index < count; index++) {
if (value == index)
kernelTotals[index][gid] = 1;
}
}
};
mapKernel.execute(Range.create(size));
mapKernel.dispose();
totals = kernelTotals;
//////////////////
// REDUCE PHASE //
//////////////////
while (size > 1) {
int nextSize = size / 2;
final int[][] currentTotals = totals;
final int[][] nextTotals = new int[count][nextSize];
Kernel reduceKernel = new Kernel() {
@Override
public void run() {
int gid = getGlobalId();
for(int index = 0; index < count; index++) {
nextTotals[index][gid] = currentTotals[index][gid * 2] + currentTotals[index][gid * 2 + 1];
}
}
};
reduceKernel.execute(Range.create(nextSize));
reduceKernel.dispose();
totals = nextTotals;
size = nextSize;
}
assert size == 1;
/////////////////////////////
// Done, just print it out //
/////////////////////////////
int[] results = new int[3];
results[0] = totals[0][0];
results[1] = totals[1][0];
results[2] = totals[2][0];
System.out.println(Arrays.toString(results));
请记住,虽然它似乎效率低下,但实际上在更大的数量上效果很好。该算法可以很好地与
size = 1048576.
使用新的大小,大约一秒钟后在我的系统上计算出以下结果。
[349602, 349698, 349276]
最后一点,您可能要考虑移至aparapi.com上更活跃的项目。与您在上面链接的较旧的库相比,它包括多个错误修复程序以及许多其他功能和性能增强。它也位于Maven中心,有大约十二个版本。因此更易于使用。我只是在此答案中编写了代码,但决定在新的Aparapi存储库的示例部分中使用它,您可以在the following link in the new Aparapi repository处找到它。