Strength reduction is the process of simplifying a program by replacing
an operation in a loop (or recursive function) with a simpler operation
using state from the previous loop iteration (or function call).
The canonical example is array indexing. Consider this snippet of C code:
struct Person {
char *name;
unsigned age;
unsigned height;
};
unsigned total_age_of_everyone (int n_people, struct Person people[])
{
unsigned total = 0;
unsigned i;
for (i=0; i<n_people; i++)
{
total += people[i].age;
}
return total;
}
Naively, the array address in the loop must be calculated
by a multiplication: the address required to access
people[i].age is:
people + i * sizeof(struct Person) + sizeof(char*)
However, on most machines, that multiplication would take a while to
calculate. At least a couple ofcycles, which is annoying since the rest
of the loop probably doesn't take much more than that to execute.
If our compiler is clever enough, and most of them are, it'll spot
that there's a faster way to calculate that address. Between each
successive iteration of the loop, that address increases by a nice,
constant value: sizeof(struct Person). So
instead of performing a slow multiplication, the compiler can just add
sizeof(struct Person) to the value that it
calculated on the last iteration.
The code the compiler produces would be equivalent to the code it would produce for:
unsigned total = 0;
unsigned i;
unsigned *age_pointer = & people[0].age;
for (i=0; i<n_people; i++)
{
total += *age_pointer;
age_pointer = (unsigned *) ( (char*) age_pointer
+ sizeof(struct Person) );
}
return total;
Note that we've introduced a loop-carried state element (person_pointer)
that wasn't there before, and that where the loop previously had an
expensive multiplication, it now only has an addition (which, on most
architectures, can probably be merged with the load operation, saving
even more on complexity).
This strength reduction can be applied to any function which
can be expressed as a simpler, iterative function. For example,
to compute a power series, we must calculate
xn (which can be quite expensive to calculate) for all values of n from 0 onwards,
but each can be calculated simply from the last value by simplying
multiplying by x.