我的生成器在其计划中使用.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);
}
};
答案 0 :(得分:2)
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);