我有在其计划中使用.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施加一个约束,即宽度是矢量化宽度的倍数。 (请注意,“ 8的倍数”与上面写的“ 8的幂”不同。)ShiftInwards限制了宽度至少是矢量化大小。 ShiftInwards在循环结束时会导致少量的冗余计算,因此不能用于归约,因为它们不是幂等的。 (即重复计算的一部分可以更改结果。)

还有一个GuardWithIf尾巴策略。这在所有情况下都是安全的,但往往会导致代码被缩放,从而导致性能下降。我们计划使用向量谓词来使这项工作更好,尽管目前尚不清楚这是否会在所有架构上推广。

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

BoundaryConditions通常需要付出一定的性能代价。希望它在通常使用中相当少,但是事实证明很难在许多硬件上使它们免费。

第二种机制是在日程表中使用specialize。这样,可以快速处理大小合适的案例,而回退到速度慢但正确的案例。通常,您将编写如下内容:

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

10-06 11:14