卤化物最优调度

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 的东西使用,所以这是它结束的合理位置。