4

私は OpenCL をいじり始めたばかりで、かなり効率的な方法でプログラムを構築する方法に行き詰まっています (主に、GPU との間、または作業が行われている場所での大量のデータ転送を回避します)。

私がやろうとしていることは、次のとおりです。

v = r*i + b*j + g*k

..とvのさまざまな値を知っていますがrgとは不明です。力ずくで/ /の妥当な値を計算したいbijkijk

言い換えれば、私は「生の」RGB ピクセル値の束を持っており、これらの色の彩度を下げたバージョンを持っています。彩度の低い値を計算するために使用される重み付け (i/j/k) がわかりません。

私の最初の計画は次のとおりでした。

  1. データを CL バッファーにロードします (したがって、入力 r/g/b 値と出力)

  2. 3 つの可能なマトリックス値と、さまざまなピクセル データ バッファーを取るカーネルがあります。

    次に を実行v = r*i + b*j + g*kし、 の値を既知の値から減算し、vこれを「スコア」バッファに格納します。

  3. 別のカーネルがその値の RMS エラーを計算します (すべての入力値の差がゼロの場合、i/j/k の値は「正しい」)

私はこれを機能させています (Python と PyCL を使用して記述されています。コードは hereです)。

私の問題は、4 つの読み取り専用バッファー (入力値用に 3 つ、期待値用に 1 つ) がありますが、i/j/k の組み合わせごとに個別の「スコア」バッファーが必要です。

もう 1 つの問題は、実効的にシングルスレッドであるため、RMS 計算が最も遅い部分であることです (「スコア」のすべての値を合計し、合計を sqrt() で計算します)。

基本的に、そのようなプログラムを構成する賢明な方法があるかどうか疑問に思っています。

これは OpenCL に適したタスクのようです。私の目標の説明が複雑すぎないことを願っています。前述のように、私の現在のコードは hereです。より明確な場合、これは私がやろうとしていることの Python バージョンです。

import sys
import math
import random


def make_test_data(w = 128, h = 128):
    in_r, in_g, in_b = [], [], []

    print "Make raw data"
    for x in range(w):
        for y in range(h):
            in_r.append(random.random())
            in_g.append(random.random())
            in_b.append(random.random())

    # the unknown values
    mtx = [random.random(), random.random(), random.random()]

    print "Secret numbers were: %s" % mtx

    out_r = [(r*mtx[0] + g*mtx[1] + b*mtx[2]) for (r, g, b) in zip(in_r, in_g, in_b)]

    return {'in_r': in_r, 'in_g': in_g, 'in_b': in_b,
            'expected_r': out_r}


def score_matrix(ir, ig, ib, expected_r, mtx):
    ms = 0
    for i in range(len(ir)):
        val = ir[i] * mtx[0] + ig[i] * mtx[1] + ib[i] * mtx[2]
        ms += abs(val - expected_r[i]) ** 2
    rms = math.sqrt(ms / float(len(ir)))
    return rms


# Make random test data
test_data = make_test_data(16, 16)


lowest_rms = sys.maxint
closest = []

divisions = 10
for possible_r in range(divisions):
    for possible_g in range(divisions):
        for possible_b in range(divisions):

            pr, pg, pb = [x / float(divisions-1) for x in (possible_r, possible_g, possible_b)]

            rms = score_matrix(
                test_data['in_r'], test_data['in_g'], test_data['in_b'], 
                test_data['expected_r'],
                mtx = [pr, pg, pb])

            if rms < lowest_rms:
                closest = [pr, pg, pb]
                lowest_rms = rms

print closest
4

2 に答える 2

1

i,j,k セットは独立していますか? はい、と思いました。パフォーマンスに影響を与えるものはほとんどありません:

  1. あまりにも多くの小さなカーネルを実行している
  2. score_matrix と rm_to_rms 間の通信にグローバル メモリを使用する

次の変更を加えることで、両方のカーネルを 1 つに書き直すことができます。

  1. 1 つの OpenCL ワークグループが異なる i、j、k で動作するようにします。これは CPU で事前に生成できます。
  2. 1を行うには、配列の複数の要素を1つのスレッドで処理する必要があります。次のように実行できます。

    int i = get_thread_id(0);
    float my_sum = 0;
    
    for (; i < array_size; i += get_local_size(0)){
        float val = in_r[i] * mtx_r + in_g[i] * mtx_g + in_b[i] * mtx_b;
        my_sum += pow(fabs(expect_r[i] - val), 2);
    }
    
  3. この後、各スレッドの my_sum をローカル メモリに書き込み、reduce (O(log(n)) アルゴリズム) で合計します。

  4. 結果をグローバルメモリに保存

または、i、j、k を順番に計算する必要がある場合は、OpenCL 仕様でバリア関数とメモリ フェンス関数を検索して、2 つのカーネルを実行する代わりにこれらを使用できます。最初のステップですべてを合計することを忘れないでください。グローバル同期すべてに書き込みます。スレッドを作成し、もう一度合計します

于 2011-08-31T18:06:43.093 に答える
1

次の 2 つの潜在的な問題があります。

  1. 各イメージの処理に必要な作業が少ない場合、カーネル起動のオーバーヘッドが大きくなる可能性があります。これはi,j,k、単一のカーネルで複数の値の評価を組み合わせることで対処できます。
  2. RMSE の合計計算のシリアル化。これは、現在、より大きな問題である可能性があります。

(2) に対処するために、合計は並列に評価できますが、入力のすべてのピクセルに対して関数を個別にマッピングするほど簡単ではないことに注意してください。これは、合計では、すべての要素を個別に処理するのではなく、隣接する要素間で値をやり取りする必要があるためです。このパターンは一般に縮小と呼ばれます。

PyOpenCL には、一般的なリダクションに対する高レベルのサポートが含まれています。ここで必要なのは合計削減です: pyopencl.array.sum(array).

これが生の OpenCL でどのように実装されているかをさらに調べると、Apple の OpenCL ドキュメントには、合計の並列削減の例が含まれています。やりたいことに最も関連する部分は、カーネルmainリダクションを実行するホスト C プログラムcreate_reduction_pass_countsの関数です。

于 2011-08-31T18:27:34.043 に答える