The polyhedral model (also called the polytope method) is a mathematical framework for programs that perform large numbers of operations -- too large to be explicitly enumerated -- thereby requiring a compact representation. Nested loop programs are the typical, but not the only example, and the most common use of the model is for loop nest optimization in program optimization. The polyhedral method treats each loop iteration within nested loops as lattice points inside mathematical objects called polyhedra, performs affine transformations or more general non-affine transformations such as tiling on the polytopes, and then converts the transformed polytopes into equivalent, but optimized (depending on targeted optimization goal), loop nests through polyhedra scanning.
Consider the following example written in C:
for (i = 1; i < n; i++)
The essential problem with this code is that each iteration of the inner loop on a[i][j]
requires that the previous iteration's result, a[i][j - 1]
, be available already. Therefore, this code cannot be parallelized or pipelined as it is currently written.
An application of the polytope model, with the affine transformation
(i',j')=(i+j,j)
In this case, no iteration of the inner loop depends on the previous iteration's results; the entire inner loop can be executed in parallel. Indeed, given a(i, j) = a[i-j][j]
then a(i, j)
only depends on a(i - 1, x)
, with
x\in\{j-1,j\}
The following C code implements a form of error-distribution dithering similar to Floyd–Steinberg dithering, but modified for pedagogical reasons. The two-dimensional array src
contains h
rows of w
pixels, each pixel having a grayscale value between 0 and 255 inclusive. After the routine has finished, the output array dst
will contain only pixels with value 0 or value 255. During the computation, each pixel's dithering error is collected by adding it back into the src
array. (Notice that src
and dst
are both read and written during the computation; src
is not read-only, and dst
is not write-only.)
Each iteration of the inner loop modifies the values in src[i][j]
based on the values of src[i-1][j]
, src[i][j-1]
, and src[i+1][j-1]
. (The same dependencies apply to dst[i][j]
. For the purposes of loop skewing, we can think of src[i][j]
and dst[i][j]
as the same element.) We can illustrate the dependencies of src[i][j]
graphically, as in the diagram on the right.
void dither(unsigned char** src, unsigned char** dst, int w, int h) |
Performing the affine transformation
(p,t)=(i,2j+i)
p
and t
instead of i
and j
, obtaining the following "skewed" routine.