有没有办法将Funcs组合成一个Func还有一个维度?

时间:2017-04-02 13:27:43

标签: halide

我从上个月开始学习Halide。 最后我遇到了大问题。

我正在尝试在Halide中实现类似C-like代码的功能。

for( int y = 0; y < 3; ++y ){
    for( int x = 0; x < 3; ++x ){
        out(x, y) = out(x-1, y-1) + 1;
    }
}

所以假设初始图像在下面。

0 0 0

 0 0 0
 0 0 0

输出图像将是...(0越界)

1 1 1
 1 2 2
 1 2 3

所以我想到了两种可能的解决方案。

·解决方法1

定义上述算法,如此递归函数。

Func algorithm1(Func input, int n)
{
    Func src, clamped, dst;
    Var x, y;

    if(n == 1){
            src = input;
    }else{
            src = algorithm1(input, n-1);
            src.compute_root();
    }

    clamped = BoundaryConditions::constant_exterior(src, 0, 0, SIZE, 0, SIZE);
    dst(x, y) = clamped(x-1, y-1) + 1;

    return dst;
}

并使用以下功能,如下面的代码。

Func input, output;
input(x, y) = src(x, y);
output = algorithm1(input, SIZE);
output.realize(src);

这种实现几乎不起作用。但显然是反击。 因为每个阶段(Func)的大多数计算结果与最终结果不匹配,尽管每个Func计算整个图像。 我需要处理更多大(正常)图像。 所以我想到了另一种可能的解决方案。

·溶液2

第一个解决方案。
声明一个函数定义一列与另一列之间的关系。

Func algorithm2(Func src)
{
    Func clamped, dst;
    Var x;

    clamped = BoundaryConditions::constant_exterior(src, 0, 0, SIZE);
    dst(x) = clamped(x-1) + 1;

    return dst;
}

然后,让我们结合这个。

Func output[3];
output[0](x) = cast<uint32_t>(0);
for(int i = 1; i < SIZE; ++i){
    output[i] = algorithm2(output[i-1]);
}

好吧......这是问题所在。如何将这个Func数组合成一个Func?

对于cource,如果我在每个func中将这个Func数组实现为列头部的指针,我就可以得到一个Image。但是,如果我想将它传递给下一个Func怎么办?

这些天我查看了整个Halide示例(测试,应用程序)。但我认为没有类似的例子。 你可能已经注意到我对英语的不适,实际上我是日本人。所以,如果有这个问题的有用例子,我很抱歉。如果是这样,请告诉我它在哪里。如果有另一个好的实施想法,请教我。无论如何,我需要别人的帮助!

感谢您的阅读。

[编辑2]

编辑1是我的愚蠢问题。我可以安排它compute_root()。 我决定把他们留在这里,但真的很尴尬。 我希望这对另一个愚蠢的人有所帮助。

[编辑1]

我很感激你心底的快速而细致的反应!

我很抱歉迟到的回复,我想在成功实施算法后回复你。但是,我的Halide代码仍然无法正常工作,并得到了一些确认。

首先,我想告诉你,由于你的缘故,我意识到了我对Halide的误解。在我算法的实现步骤的第一步,我只使用纯'Var'编写了定义。 所以我得到了以下错误。

All of a functions recursive references to itself must contain the same pure variables in the same places as on the left-hand-side.

我认为由于调度灵活性而发生此错误。如果允许这样的定义并安排它进行拆分,则意味着调度更改算法。这种理解是正确的吗?从这样的理解,虽然我已经阅读了教程和模糊示例的减少部分,但我误解了我无法访问所有Func定义中的邻居像素。我不知道为什么。

由于同样的原因,还原域无法拆分。我想我现在知道了。

这是您的代码的另一个问题。感谢你的Halide实现示例,我几乎成功地实现了我想做的事情而没有考虑。然而,尽管我正在处理20x20裁剪图像以便于调试,但这种实现速度非常慢。

我认为这种缓慢是由缩减域引起的。在您的示例中,例如,在计算值g(10,10)时,Halide计算从f(0,0)调度到f(0,0),最后得到值。另一方面,C实现只是将值加载到g(9,9)并且只是递增它。我们可以从打印循环嵌套确认这样的计算。

produce g:
  for y:
    for x:
      produce f:
        for y:
          for x:
            f(...) = ...
        for range:
          for range:
            f(...) = ...
      consume f:
        g(...) = ...

我想确认避免这种重新计算是不可能的?所以你建议了吗?

我想问你另一个简单的问题。如果存在这样的反向依赖,

for( int y = 2; y > 0; --y ){
    for( int x = 2; x > 0; --x ){
        out(x, y) = out(x+1, y+1) + 1;
    }
}

Halide是否能够表达此代码?

1 个答案:

答案 0 :(得分:1)

这里的算法1和算法2部分对我来说不是很清楚。我理解最初的问题陈述和英语似乎很好,所以我会努力提供一些帮助来回答我认为你问的问题。我将通过说明一些您可能不知道或在此处不明显的Halide机制来做到这一点。希望这会有所帮助。

首先,要将Halide Func的维度映射到不同的表达式,您几乎必须使用select语句:

Var x, y, n;
Func f_0, f_1, f_both;
f_0(x, y) = ...;
f_1(x, y) = ...;
f_both(x, y, n) = select(n == 0, f_zero, f_one);

通过向select添加参数,可以将其扩展到更多情况。这对于分段计算比对递归结构更有用,但似乎是标题中问题的最直接答案。

第二种机制是Tuple。这允许Func具有多个值,可以使用编译时常量索引。我不认为这是你正在寻找的答案,但我在tutorial / lesson_13_tuples.cpp中有所论述。

最后,Halide支持减少,这是为了处理第一个代码示例中的情况而设计的。这看起来像这样:

Var x, y;
Func f, g;
RDom range(0, 3, 0, 3); // Form is min/extent, not start/end

f(x, y) = 0; // Initial condition
f(range.x, range.y) = f(range.x - 1, range.y - 1) + 1;

g(x, y) = f(x, y);

Buffer<int32t> result = g.realize(3, 3);

这应该会产生第一个例子的输出。教程/ lesson_09_update_definitions.cpp中介绍了缩减或“更新定义”。