卤化物最优调度
Halide optimal scheduling
我正在尝试为基准 Halide 代码制定最佳时间表,但我可能遗漏了一些东西,因为计时结果对我来说意义不大。
我使用的是AOT编译,下面是代码的算法部分:
ImageParam input1(type_of<float>(), 3);
ImageParam input2(type_of<float>(), 3);
Func in1 = BoundaryConditions::constant_exterior(input1, 0.0f);
Func in2 = BoundaryConditions::constant_exterior(input2, 0.0f);
f1(x, y, z) = (in1(x + 1, y, z) + in1(x, y, z) + in1(x - 1, y,z));
f2(x, y, z) = (in2(x + 2, y, z) + in2(x + 1, y, z) + in2(x, y, z) +in2(x - 1, y, z) + in2(x - 2, y, z));
res(x, y, z) = f1(x, y, z) + f1(x - 1, y, z) + f2(x - 1, y, z) + f2(x, y, z);
我的时间表是这样的:
f1.store_at(res, y).compute_at(res, yi).vectorize(x, 8);
f2.store_at(res, y).compute_at(res, yi).vectorize(x, 8);
res.split(y, y, yi, 8).vectorize(x, 8).parallel(y);
res.print_loop_nest();
我使用 current_time 函数来计时我的代码的执行。当我对 f1 和 f2 使用上述计划时,执行时间比我仅对其中一个 Func 使用计划时要长。考虑到模板的结构,我认为同时安排它们会提高性能。我在这里错过了什么?此外,当我打印循环以查看生成的代码时:
for k:
parallel j.j:
store f1:
store f2:
for j.in_y in [0, 7]:
produce f1:
for k:
for j:
for i.i:
vectorized i.v122 in [0, 7]:
f1(...) = ...
consume f1:
produce f2:
for k:
for j:
for i.i:
vectorized i.v126 in [0, 7]:
f2(...) = ...
consume f2:
for i.i:
vectorized i.v133 in [0, 7]:
result(...) = ...
consume result:
只是缩进还是产品 f2 嵌套在产品 f1 中?
对更好的时间表有什么建议吗?
我认为这可能是内存带宽限制。将 f1 或 f2 内联到 res 所暗示的一些额外添加并不重要。事实上,我通过以下时间表获得了最佳表现:
in1.compute_at(res, yi).vectorize(in1.args()[0], 8);
in2.compute_at(res, yi).vectorize(in2.args()[0], 8);
res.split(y, y, yi, 8).vectorize(x, 8).parallel(y);
即只需拉入每个输入的填充扫描线,然后内联进行所有数学运算。
但它几乎不比你的快。差异可能是噪音。我的完整实验:
https://gist.github.com/abadams/c2e6f67d79e1768af6db5afcabb1caab
f2 的产品嵌套在 f1 的 消费 中。这很正常 - 它不使用 f1,但它被确实使用 f1 的东西使用,所以这是它结束的合理位置。
我正在尝试为基准 Halide 代码制定最佳时间表,但我可能遗漏了一些东西,因为计时结果对我来说意义不大。
我使用的是AOT编译,下面是代码的算法部分:
ImageParam input1(type_of<float>(), 3);
ImageParam input2(type_of<float>(), 3);
Func in1 = BoundaryConditions::constant_exterior(input1, 0.0f);
Func in2 = BoundaryConditions::constant_exterior(input2, 0.0f);
f1(x, y, z) = (in1(x + 1, y, z) + in1(x, y, z) + in1(x - 1, y,z));
f2(x, y, z) = (in2(x + 2, y, z) + in2(x + 1, y, z) + in2(x, y, z) +in2(x - 1, y, z) + in2(x - 2, y, z));
res(x, y, z) = f1(x, y, z) + f1(x - 1, y, z) + f2(x - 1, y, z) + f2(x, y, z);
我的时间表是这样的:
f1.store_at(res, y).compute_at(res, yi).vectorize(x, 8);
f2.store_at(res, y).compute_at(res, yi).vectorize(x, 8);
res.split(y, y, yi, 8).vectorize(x, 8).parallel(y);
res.print_loop_nest();
我使用 current_time 函数来计时我的代码的执行。当我对 f1 和 f2 使用上述计划时,执行时间比我仅对其中一个 Func 使用计划时要长。考虑到模板的结构,我认为同时安排它们会提高性能。我在这里错过了什么?此外,当我打印循环以查看生成的代码时:
for k:
parallel j.j:
store f1:
store f2:
for j.in_y in [0, 7]:
produce f1:
for k:
for j:
for i.i:
vectorized i.v122 in [0, 7]:
f1(...) = ...
consume f1:
produce f2:
for k:
for j:
for i.i:
vectorized i.v126 in [0, 7]:
f2(...) = ...
consume f2:
for i.i:
vectorized i.v133 in [0, 7]:
result(...) = ...
consume result:
只是缩进还是产品 f2 嵌套在产品 f1 中? 对更好的时间表有什么建议吗?
我认为这可能是内存带宽限制。将 f1 或 f2 内联到 res 所暗示的一些额外添加并不重要。事实上,我通过以下时间表获得了最佳表现:
in1.compute_at(res, yi).vectorize(in1.args()[0], 8);
in2.compute_at(res, yi).vectorize(in2.args()[0], 8);
res.split(y, y, yi, 8).vectorize(x, 8).parallel(y);
即只需拉入每个输入的填充扫描线,然后内联进行所有数学运算。
但它几乎不比你的快。差异可能是噪音。我的完整实验:
https://gist.github.com/abadams/c2e6f67d79e1768af6db5afcabb1caab
f2 的产品嵌套在 f1 的 消费 中。这很正常 - 它不使用 f1,但它被确实使用 f1 的东西使用,所以这是它结束的合理位置。