3

私は主に非常に高レベルのプログラマーなので、CPU の局所性などについて考えるのは私にとって非常に新しいことです。

私は基本的なバイリニア デモザイク (RGGB センサー データ用) に取り組んでおり、アルゴリズムは正しい (結果から判断すると) ですが、期待どおりのパフォーマンスが得られません (~210Mpix/s)。

これが私のコードです(入力はRGGBの単一チャネルを持つ4640x3472画像です):

def get_bilinear_debayer(input_raw, print_nest=False):
    x, y, c = Var(), Var(), Var()

    # Clamp and move to 32 bit for lots of space for averaging.
    input = Func()
    input[x,y] = cast(
        UInt(32),
        input_raw[
            clamp(x,0,input_raw.width()-1),
            clamp(y,0,input_raw.height()-1)]
    )

    # Interpolate vertically
    vertical = Func()
    vertical[x,y] = (input[x,y-1] + input[x,y+1])/2

    # Interpolate horizontally
    horizontal = Func()
    horizontal[x,y] = (input[x-1,y] + input[x+1,y])/2

    # Interpolate on diagonals
    diagonal_average = Func()
    diagonal_average[x, y] = (
        input[x+1,y-1] + 
        input[x+1,y+1] +
        input[x-1,y-1] +
        input[x-1,y+1])/4

    # Interpolate on adjacents
    adjacent_average = Func()
    adjacent_average[x, y] = (horizontal[x,y] + vertical[x,y])/2

    red, green, blue = Func(), Func(), Func()

    # Calculate the red channel
    red[x, y, c] = select(
        # Red photosite
        c == 0, input[x, y],
        # Green photosite
        c == 1, select(x%2 == 0, vertical[x,y],
                                 horizontal[x,y]),
        # Blue photosite
        diagonal_average[x,y]
    )

    # Calculate the blue channel
    blue[x, y, c] = select(
        # Blue photosite
        c == 2, input[x, y],
        # Green photosite
        c == 1, select(x%2 == 1, vertical[x,y],
                                 horizontal[x,y]),
        # Red photosite
        diagonal_average[x,y]
    )

    # Calculate the green channel
    green[x, y, c] = select(
        # Green photosite
        c == 1, input[x,y],
        # Red/Blue photosite
        adjacent_average[x,y]
    )

    # Switch color interpolator based on requested color.
    # Specify photosite as third argument, calculated as [x, y, z] = (0, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 2)
    # Happily works out to a sum of x mod 2 and y mod 2.
    debayer = Func()
    debayer[x, y, c] = select(c == 0, red[x, y, x%2 + y%2],
                              c == 1, green[x, y, x%2 + y%2],
                                      blue[x, y, x%2 + y%2])


    # Scheduling
    x_outer, y_outer, x_inner, y_inner, tile_index = Var(), Var(), Var(), Var(), Var()

    bits = input_raw.get().type().bits

    output = Func()
    # Cast back to the original colour space
    output[x,y,c] = cast(UInt(bits), debayer[x,y,c])
    # Reorder so that colours are calculated in order (red runs, then green, then blue)

    output.reorder_storage(c, x, y)
    # Tile in 128x128 squares
    output.tile(x, y, x_outer, y_outer, x_inner, y_inner, 128, 128)
    # Vectorize based on colour
    output.bound(c, 0, 3)
    output.vectorize(c)
    # Fuse and parallelize
    output.fuse(x_outer, y_outer, tile_index)
    output.parallel(tile_index)

    # Debugging
    if print_nest:
        output.print_loop_nest()
        debayer.print_loop_nest()
        red.print_loop_nest()
        green.print_loop_nest()
        blue.print_loop_nest()

    return output

正直なところ、私はここで何をしているのかわかりません。また、これに慣れていないため、どこを何を見ればよいのかわかりません。

スケジューリングを改善する方法についてのアドバイスは役に立ちます。私はまだ学んでいますが、フィードバックを見つけるのは難しいです。

私が持っているスケジュールは私ができる最高のものですが、それはほとんど完全に試行錯誤です.

編集:関数内の隣接する平均合計全体を直接実行し、色の代わりに x_inner でベクトル化することにより、余分な 30Mpix/s を追加しました。

編集: 新しいスケジュール:

# Set input bounds.
output.bound(x, 0, (input_raw.width()/2)*2)
output.bound(y, 0, (input_raw.height()/2)*2)
output.bound(c, 0, 3)

# Reorder so that colours are calculated in order (red runs, then green, then blue)
output.reorder_storage(c, x, y)
output.reorder(c, x, y)

# Tile in 128x128 squares
output.tile(x, y, x_outer, y_outer, x_inner, y_inner, 128, 128)
output.unroll(x_inner, 2).unroll(y_inner,2)

# Vectorize based on colour
output.unroll(c)
output.vectorize(c)

# Fuse and parallelize
output.fuse(x_outer, y_outer, tile_index)
output.parallel(tile_index)

編集: 最終的なスケジュールは、私の2 倍の CPU で実行されたIntel Performance Primitive ベンチマーク(640MP/s) を上回っています:

output = Func()

# Cast back to the original colour space
output[x,y,c] = cast(UInt(bits), debayer[x,y,c])

# Set input bounds.
output.bound(x, 0, (input_raw.width()/2)*2)
output.bound(y, 0, (input_raw.height()/2)*2)
output.bound(c, 0, 3)

# Tile in 128x128 squares
output.tile(x, y, x_outer, y_outer, x_inner, y_inner, 128, 128)
output.unroll(x_inner, 2).unroll(y_inner, 2)

# Vectorize based on colour
output.vectorize(x_inner, 16)

# Fuse and parallelize
output.fuse(x_outer, y_outer, tile_index)
output.parallel(tile_index)

target = Target()
target.arch = X86
target.os = OSX
target.bits = 64
target.set_feature(AVX)
target.set_feature(AVX2)
target.set_feature(SSE41)

output.compile_jit(target)
4

1 に答える 1

4

チャネルごとの選択ロジックを最適化するために unroll(c) を使用していることを確認してください。x と y を 2 ずつ展開することも役立ちます。

output.unroll(x, 2).unroll(y,2)

目標は、偶数/奇数の行と列の間の選択ロジックを最適化することです。これを最大限に活用するには、最小値と範囲が 2 の倍数であることを Halide に伝える必要があります。

output.output_buffer().set_bounds(0,
                                  (f.output_buffer().min(0) / 2) * 2,
                                  (output.output_buffer().extent(0) / 2) * 2)
output.output_buffer().set_bounds(1,
                                  (f.output_buffer().min(1) / 2) * 2,
                                  (output.output_buffer().extent(1) / 2) * 2)

タイル サイズの倍数をアサートするために 2 ではなく 128 を使用したり、単一のカメラのみをサポートしている場合に実際のセンサー パラメーターを反映するように最小値と範囲を配線するなど、さらに厳しい制約を述べる価値があるかもしれません。

于 2015-10-24T07:46:46.553 に答える