我有在其计划中使用.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
,这是还原的默认行为。非缩减的默认值为ShiftInwards
。 RoundUp
施加一个约束,即宽度是矢量化宽度的倍数。 (请注意,“ 8的倍数”与上面写的“ 8的幂”不同。)ShiftInwards
限制了宽度至少是矢量化大小。 ShiftInwards
在循环结束时会导致少量的冗余计算,因此不能用于归约,因为它们不是幂等的。 (即重复计算的一部分可以更改结果。)
还有一个GuardWithIf
尾巴策略。这在所有情况下都是安全的,但往往会导致代码被缩放,从而导致性能下降。我们计划使用向量谓词来使这项工作更好,尽管目前尚不清楚这是否会在所有架构上推广。
还有其他两种机制需要了解。第一个是BoundaryConditions
。这就是您提到夹具的想法。 (BoundaryConditions
函数的核心是基于clamp
的功能,但是它们还做一些其他事情来帮助编译器,使代码更清晰。)将BoundaryConditions
视为正确性问题,而不是性能一。当给定输出的输入量不足时,您希望算法做什么?一旦您决定了正确的事情,就可以通过BoundaryConditions
来实现,或者在某些情况下可以忽略,因为它不允许发生。BoundaryConditions
通常需要付出一定的性能代价。希望它在通常使用中相当少,但是事实证明很难在许多硬件上使它们免费。
第二种机制是在日程表中使用specialize
。这样,可以快速处理大小合适的案例,而回退到速度慢但正确的案例。通常,您将编写如下内容:
f.specialize(input.width() % 8 == 0).vectorize(x, 8);