0

ステンシルの計算を最適化するために Halide を使用していますが、スケジューリングの部分はちょっと難しいです! これが私の Halide コードで、AOT コンパイルを使用しています。

//Halide declarations:
Param<int> max_x, max_y, max_z; 
Param<float> r; 
Var x("x"), y("y"), z("z"), s, xi("xi"), yi("yi"), m("n"), xo("xo"), yo("yo"), index("idx");
Func f1_unbound("f_ub"), f1_bound("f_b"), result("res");

ImageParam input1(type_of<float>(), 3);
ImageParam input2(type_of<float>(), 3);

Expr t_h;

//The algorithm:
t_h = input2(x, y, z) / (r * input1(x, y, z));
f1_unbound(x, y, z) = 100.0f / (t_h) * pow((t_h / 200.0f), 1.5f); 
f1_bound(x, y, z) =  BoundaryConditions::repeat_edge(f1_unbound, 1, max_x - 2, 1, max_y - 2, 1, max_z - 2)(x, y, z);
result(x, y, z) = 0.125f * (f1_bound(x, y, z) + f1_bound(x + 1, y, z) +
                            f1_bound(x, y + 1, z) + f1_bound(x + 1, y + 1, z) + 
                            f1_bound(x, y, z + 1) + f1_bound(x + 1, y, z + 1) +
                            f1_bound(x, y + 1, z + 1) + f1_bound(x + 1, y + 1, z + 1));

f1_bound.split(x, x, xi, 32).unroll(y, 2).unroll(xi, 2).vectorize(xi, 16).compute_at(result, y).store_at(result, y);
//f1_unbound.compute_root();
//f1_bound.vectorize(x, 16);
result.tile(x, y, x, y, xi, yi, 32, 8).vectorize(xi, 16).bound(x, 0, ((max_x + 1) / 2) * 2).bound(y, 0, ((max_y + 1) / 2) * 2).bound(z, 0, (max_z + 1)).parallel(y);
result.print_loop_nest();

result.compile_to_static_library("v_halide", {input1, input2, r, max_x, max_y, max_z}, "v_halide");
std::cout << "Compiled to static library!" << std::endl;

分割/タイリング、および各関数の評価場所の指定に関して、スケジュールが異なるとパフォーマンスがどのように変化するかを理解しています。ただし、並列パフォーマンスにはいくつかの問題があります。上記のような、並列化のためのさまざまなスケジューリングを試しました。しかし、効率的な並列スケジュールを見つけることができず、プロファイリングの数値はやや混乱しています。上記のスケジュールでは、スレッドの数が増えると、「結果」が遅くなり、f1_bound が速くなりますが、スレッドとして報告される数 (私が間違っていなければ、各リージョンのアクティブなスレッドの平均数です) は両方で増加します。

4 スレッド:
平均使用スレッド数: 3.586322

ヒープ割り当て: 19500 ピーク時のヒープ使用量: 116640 バイト

解像度: 0.946ms (33%) スレッド数: 3.119

f1_b: 1.873ms (66%) スレッド: 3.823 ピーク: 116640 数値: 19500 平均: 29160

2 スレッド: 平均使用スレッド数: 1.934264

ヒープ割り当て: 19500 ピーク時のヒープ使用量: 58320 バイト

解像度: 0.769ms (19%) スレッド数: 1.794

f1_b: 3.152ms (80%) スレッド: 1.968 ピーク: 58320 数値: 19500 平均: 29160

f1_bound と unbound の両方をスケジュールすると、スレッド数が増えるにつれてスケーリングが向上しますが、局所性が少ないと思うので、単一スレッドのコードは並列化しない場合よりも遅くなります。

f1_bound.split(x, x, xi, 32).unroll(y, 2).unroll(xi, 2).vectorize(xi, 16).compute_at(result, y);
f1_unbound.split(x, x, xi, 32).unroll(y, 2).unroll(xi, 2).vectorize(xi, 16).compute_at(result, y).store_at(result, y);

より良いスケジュールのための提案はありますか?

4

0 に答える 0