对宽度不匹配的输出缓冲区进行矢量化

Vectorize for output buffers with mismatching width

我的发电机在其时间表中使用 .vectorize(x, 8)。我面临的问题是,如果我的输出缓冲区宽度不是 8 的幂,我将在缓冲区外访问!我当然可以将输入 x, y 限制为图像的大小,但我想知道是否有任何方法可以在我的生成器中使用 Output<Func> 来做到这一点。可能是我看问题的方式不对?

class BasicGenerator : public Generator<BasicGenerator>
{
public:
    Var x, y;

    Input<Func> input { "input", UInt(8), 2 }
    Output<Func> output { "output", UInt(8), 2 }

    void generate()
    {
        output(x, y) = input(x, y);
    }

    void schedule()
    {
        output.vectorize(x, 8).parallel(y);
    }
};

vectorize 指令采用 TailStrategy 参数。这控制如何处理矢量化范围的末端。您所描述的行为似乎是 RoundUp,这是减少的默认行为。非减少的默认值为 ShiftInwardsRoundUp 施加了一个约束,即宽度是向量化宽度的倍数。 (请注意,"multiple of 8" 与上面写的 "power of 8" 不同。)ShiftInwards 强加了宽度至少为矢量化大小的约束。 ShiftInwards 在循环结束时导致少量冗余计算,因此不能用于归约,因为它们不是幂等的。 (即重复部分计算可以改变结果。)

还有一个GuardWithIf尾策略。这在所有情况下都是安全的,但往往会导致代码被标量化,从而降低性能。我们计划使用矢量预测来使这项工作更好,但尚不清楚这是否会在所有架构上都适用。

还有两种机制需要了解。第一个是 BoundaryConditions。这就是您在提到夹子时所想到的。 (BoundaryConditions 函数的核心是基于 clamp,但它们还做了一些其他事情来帮助编译器,应该使代码更清晰。)将 BoundaryConditions 视为正确性问题,而不是性能问题。当给定输出没有足够的输入时,您希望您的算法做什么?一旦您决定了正确的事情,就可以通过 BoundaryConditions 实施,或者在某些情况下简单地忽略它,因为它是不允许发生的。

BoundaryConditions 通常以性能为代价。希望它在普通使用中相当少,但事实证明很难在很多硬件上免费使用它们。

第二种机制是在时间表中使用specialize。这允许快速处理大小合适的案例,同时为不合适的案例回退到较慢但正确的代码。通常你会这样写:

f.specialize(input.width() % 8 == 0).vectorize(x, 8);