ここに問題があります。 ++演算子は実際には1つの演算で3つあります:現在の値を読み込み、インクリメントして新しい値を書き込みます。 Aparapiでは、潜在的に1024のGPUスレッドを同時に実行します。これは、値が0のときと同じように値を読み取ってから1に増やすと、1024のスレッドすべてが1を書き込むことを意味します。したがって、期待どおりに動作しています。
あなたがしようとしていることは、マップリダクション機能と呼ばれています。あなたは多くのステップをスキップしています。あなたは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.
新しいサイズでは、次の結果が約1秒で私のシステムで計算されました。
[349602, 349698, 349276]
最後の注意点は、あなたがaparapi.comで、よりアクティブなプロジェクトへの移行を検討する必要があります。これには、バグに対するいくつかの修正が含まれています。上にリンクした古いライブラリには、多くの追加機能とパフォーマンスの強化が含まれています。それはまた約12のリリースで中心的な場所にあります。使い方が簡単です。私はちょうどこの答えでコードを書いたが、新しいAparapiリポジトリのサンプルセクションでそれを使用することに決めた。それはthe following link in the new Aparapi repositoryにある。
書き込み競合状態。あなたは原子インクリメント関数が必要です。 –