2016-08-04 14 views
0

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(); 

このコードセグメント、私はカウント[]の値を印刷するとき、それは私に1,1,1を与えます。 V [gid]ごとにcount[V[gid]]++が1回だけ実行されるようです。

ありがとうございました。

+0

書き込み競合状態。あなたは原子インクリメント関数が必要です。 –

答えて

0

ここに問題があります。 ++演算子は実際には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にある。

関連する問題