2

APARAPIと並行して Entropy 関数を実装したいと考えています。その関数では、ベクトル内のさまざまなキーをカウントする必要がありますが、正しく実行できません。

異なる値が 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 回だけ実行されるようです。

ありがとう。

4

1 に答える 1

2

ここに問題があります。++ 演算子は、実際には 3 つの操作を 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.

新しいサイズでは、次の結果が私のシステムで約 1 秒で計算されました。

[349602, 349698, 349276]

最後に、 aparapi.comのよりアクティブなプロジェクトに移行することを検討してください。バグに対するいくつかの修正と、上でリンクした古いライブラリに対する多くの追加機能とパフォーマンスの強化が含まれています。また、約12のリリースでmaven centralにもあります。そのため、使いやすくなっています。この回答でコードを書きましたが、新しい Aparapi リポジトリのサンプル セクションで使用することにしました。新しい Aparapi リポジトリの次のリンクで確認できます。

于 2016-12-29T12:46:10.003 に答える