使用 Aparapi 计算向量中的不同值

counting different values in a vector with Aparapi

我想与 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. It includes several fixes to bugs and a lot of extra features and performance enhancements over the older library you linked above. It is also in maven central with about a dozen releases. so it is easier to use. I just wrote the code in this answer but decided to use it in the new Aparapi repository's example section, you can find that at the following link in the new Aparapi repository 上更活跃的项目。