halide/Halide — pull request #495 by white-pony (2014-10-07 07:32:47+00:00)
Title: Performance issue with compute_at + bound

I am not sure how often this happens in practice (I don't see why it may be useful), but it seems that there is a performance issue when bound() is used with compute_at():
```c++
#include
#include

using namespace Halide;

int main(int argc, char **argv) {
Var x("x"), y("y");
Func f("f"), g("g");

Image input(2048, 2048);
for(int iy = 0; iy < 2048; iy++)
{
for (int ix = 0; ix < 2048; ix++) {
input(ix, iy) = (float)rand();
}
}

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

f.bound(x, 0, 2048);
f.bound(y, 0, 2048);
f.compute_at(g, y);

Image img = g.realize(2048, 2048);

printf("Success!\n");
return 0;
}
```
following code is generated:
```c
produce g {
for (g.s0.y, g.min.1, g.extent.1) {
let f.y.min_realized = min(g.s0.y, 0)
let f.y.extent_realized.s.s = max(g.s0.y, 2047)
let f.x.min_realized = min(g.min.0, 0)
let f.x.extent_realized.s.s = max(((g.extent.0 + g.min.0) + -1), 2047)
allocate f[float32 * ((f.x.extent_realized.s.s - f.x.min_realized) + 1) * ((f.y.extent_realized.s.s - f.y.min_realized) + 1)]
produce f {
for (f.s0.y, 0, 2048) {
for (f.s0.x, 0, 2048) {
f[((f.s0.x - f.x.min_realized) + ((f.s0.y - f.y.min_realized)*((f.x.extent_realized.s.s - f.x.min_realized) + 1)))] = input[(f.s0.x + (f.s0.y*2048))]
}
}
}
for (g.s0.x, g.min.0, g.extent.0) {
g[((g.s0.x + (g.s0.y*g.stride.1)) - (g.min.0 + (g.min.1*g.stride.1)))] = (float32((g.s0.x + g.s0.y)) + f[((g.s0.x - f.x.min_realized) + ((g.s0.y - f.y.min_realized)*((f.x.extent_realized.s.s - f.x.min_realized) + 1)))])
}
free f
}
}
```
which does not look very nice, as there are three loops (I expect to have two). However, after commenting line:
```c++
f.bound(y, 0, 2048);
```
correct code is generated:
```c
produce g {
for (g.s0.y, g.min.1, g.extent.1) {
let f.x.min_realized = min(g.min.0, 0)
let f.x.extent_realized.s.s = max(((g.extent.0 + g.min.0) + -1), 2047)
allocate f[float32 * ((f.x.extent_realized.s.s - f.x.min_realized) + 1) * 1]
produce f {
for (f.s0.x, 0, 2048) {
f[(f.s0.x - f.x.min_realized)] = input[(f.s0.x + (g.s0.y*2048))]
}
}
for (g.s0.x, g.min.0, g.extent.0) {
g[((g.s0.x + (g.s0.y*g.stride.1)) - (g.min.0 + (g.min.1*g.stride.1)))] = (float32((g.s0.x + g.s0.y)) + f[(g.s0.x - f.x.min_realized)])
}
free f
}
}
```

I am not sure why exactly it happens, but it seems that when bounds are set for a function, another set of min/max lets are generated with certain bounds values, so it 'overrides' what will be set later according to the compute_at rule (please, ignore if it does not make sense).

I doubt that this fix is the best one, but here I just ignore bounds for vars from outer loop (relatively to a current function loop level)