[comp.lang.c] induction variable optimization

dean@ndmath.UUCP (02/02/87)

In a recent Newsletter from Microsoft in Byte, the following is given as an
example of *induction variable optimization* :

     for(i=0;i<10;++i)   j += i*7;

     evaluates to:

     for(i=0;i<70;i += 7)  j += i;

Now, I may not understand what "evaluates to" means here, but it seems to me
that these statements are not equivalent, and following code which depends on
the value of i may not work correctly. Am I misunderstanding something?

     Dean Alvis   ( ...!ndmath!dean)

ballou@brahms.Berkeley.EDU.UUCP (02/03/87)

In article <182@ndmath.UUCP> dean@ndmath.UUCP (Dean Alvis) writes:
>In a recent Newsletter from Microsoft in Byte, the following is given as an
>example of *induction variable optimization* :
>
>     for(i=0;i<10;++i)   j += i*7;
>
>     evaluates to:
>
>     for(i=0;i<70;i += 7)  j += i;

	Well, not quite.  If this is what the newsletter indicates, then
the newsletter is in error.  The idea is to avoid doing a multiplication
by 7, and the correct idea is this:

Replace
	for (i = 0; i < 10; ++ i)  j += i * 7;

with
	i = 10;
	for ($foo = 0; $foo < 70; $foo += 7)  j += $foo;

WHERE $foo is a compiler generated temporary whose lifetime is the for loop.
(Aside:  Please, no flames, I am quite well aware that '$' cannot appear in
a C identifier!)  This works as long as the compiler can prove that the
value of i will not be altered by side effects in the loop.

>Now, I may not understand what "evaluates to" means here, but it seems to me
>that these statements are not equivalent, and following code which depends on
>the value of i may not work correctly. Am I misunderstanding something?

	(Your "following code" appears to have turned into line eater fodder.)
Yes, you are right, the code you indicated does not work.  However, the
suggested replacement does have the same effect.



--------
Kenneth R. Ballou			ARPA:  ballou@brahms.berkeley.edu
Department of Mathematics		UUCP:  ...!ucbvax!brahms!ballou
University of California
Berkeley, California  94720

braner@batcomputer.UUCP (02/03/87)

[]

The full-page ad by Microsoft in BYTE tells us about all kinds of nifty
optimizations that a C compiler MIGHT do if it was smart enough.

It would be a lot more interesting to hear about ACTUAL optimizations
done by REAL compilers.  I would be especially interested in hearing about
pitfalls, e.g. correct but obtuse code that confuses a given optimizing
compiler into generating wrong code...

- Moshe Braner

m5d@bobkat.UUCP (02/03/87)

In article <182@ndmath.UUCP> dean@ndmath.UUCP (Dean Alvis) writes:
>In a recent Newsletter from Microsoft in Byte, the following is given as an
>example of *induction variable optimization* :
>
>     for(i=0;i<10;++i)   j += i*7;
>
>     evaluates to:
>
>     for(i=0;i<70;i += 7)  j += i;
>
>Now, I may not understand what "evaluates to" means here, but it seems to me
>that these statements are not equivalent, and following code which depends on
>the value of i may not work correctly. Am I misunderstanding something?
>
>     Dean Alvis   ( ...!ndmath!dean)

The two statements are equivalent in terms of the effect on "j".  If
the optimizer determines that the variable "i" is not live after the
loop (i.e., there are no references to "i" and no pointers pointing to
it (hard to determine)) then the optimization is permissable...sort
of.  This is one of those things that would have to be a selectable
optimization.  If "i" were actually a hardware register in some
controller somewhere, then this might cause problems.

The compiler might also decide to generate the second loop, then insert
code afterwards to "fix" the value of "i".

Most induction variable oportunities arise from array subscripting:

    long a[100];

    for (i = 0; i < 100; i++)
        a[i] = <something>;

The compiler will have to generate code to multiply "i" by sizeof(long)
each time through the loop.  This could be improved by optimization,
although most C programmers would do it in the source code:

    long a[100], *b;

    for (i = 0, b = a; i < 100; i++, *b++ = <something>);

I once heard a story from a compiler deity in which some dude wrote a
FORTRAN source code optimizer.  It read FORTRAN source and produced
FORTRAN source which was "better".  It supposedly did better than the
IBM G (or H, whatever the heavily optimizing IBM FORTRAN compiler was
called) optimizer.  This could be fiction.

-- 
Mike McNally, mercifully employed at Digital Lynx ---
    Where Plano Road the Mighty Flood of Forest Lane doth meet,
    And Garland fair, whose perfumed air flows soft about my feet...
uucp: {texsun,killer,infotel}!pollux!bobkat!m5d (214) 238-7474

gwyn@brl-smoke.UUCP (02/08/87)

In article <182@ndmath.UUCP> dean@ndmath.UUCP (Dean Alvis) writes:
>     for(i=0;i<10;++i)   j += i*7;
>  evaluates to:
>     for(i=0;i<70;i += 7)  j += i;
>... following code which depends on the value of i may not work correctly.

I suspect the article in question was merely using C to illustrate
the concept, not claiming formal equivalence under all circumstances.
Such optimization is usually done at a point inside the compiler where
the original C code no longer exists but has been turned into parse
trees.  Certainly, correct optimization would require fixing up the
final value of variable `i', if it were used after the loop.

Note that experienced C programmers often have already performed such
program transformations in their heads and written the "optimized"
code sequence; this is partly due to the fact that the original C
compilers didn't do much optimization.  So long as an optimization is
guaranteed never to alter program semantics, it really should be done
by the compiler rather than by the programmer, who generally has more
important things to worry about.

guy@gorodish.UUCP (02/09/87)

>So long as an optimization is guaranteed never to alter program semantics,
>it really should be done by the compiler rather than by the programmer, who
>generally has more important things to worry about.

And who is, more often than not these days, writing C code rather
than writing code that happens to be in C for some particular
machine (even when writing kernel code!), so that they *can't* know
what code is better or worse.

bright@dataio.UUCP (02/09/87)

In article <5603@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <182@ndmath.UUCP> dean@ndmath.UUCP (Dean Alvis) writes:
>>     for(i=0;i<10;++i)   j += i*7;
>>  evaluates to:
>>     for(i=0;i<70;i += 7)  j += i;
>>... following code which depends on the value of i may not work correctly.
>I suspect the article in question was merely using C to illustrate
>the concept, not claiming formal equivalence under all circumstances.
>Certainly, correct optimization would require fixing up the
>final value of variable `i', if it were used after the loop.
	There are a lot of subtle things about loop induction variable
	replacement, the point of Microsoft's article was merely to
	advertise that their next version of the compiler could
	do it. For a good treatment of the subject (though not
	complete, as I discovered), see Aho and Ullman's excellent
	book on compilers (usually known as the Dragon Book).
>Note that experienced C programmers often have already performed such
>program transformations in their heads and written the "optimized"
>code sequence; this is partly due to the fact that the original C
>compilers didn't do much optimization.  So long as an optimization is
>guaranteed never to alter program semantics, it really should be done
>by the compiler rather than by the programmer, who generally has more
>important things to worry about.
	You are quite right. In fact, nearly all the transformations
	done by an optimizing compiler can be done by the programmer
	in the original source code. The reasons this is undesirable is:

	1. The goals of maximum performance vs portability, readability
	   and maintainability are often at right angles to each other.

	2. 'Tuning' code to wring the last bit of performance out of
	   your code may work against you when using a different compiler
	   or compiling for a different CPU.

	Having a global optimizer enables the programmer to concentrate
	on the algorithm rather than performance, letting the compiler
	do the grunt work.

	By the way, Datalight is the first C compiler vendor for the PC
	that is shipping a global optimizer, see the Feb. 87 Computer
	Language ad. Datalight can be reached at (206) 367-1803.

	P.S. I have no connection with Datalight other than the fact that
	I wrote the compiler, and make money off of sales of it!

mccaugh@uiucdcsm.UUCP (02/11/87)

 Yes, the two fragments of code are semantically equivalent in that they
 should yield as final value j(final) = j(initial) + 315. Of course, if
 j(initial) = undefined, they will still be equivalent!
 A loop-invariant for the first fragment is: 
       (i=#) & (j(#) = j(initial) + 7*(1 + ... + #-1))
 where '#' = number of cycles (of the FOR-loop); a loop-invariant for the
 second fragment is similar: (i=7*#) & (j(#) = j(initial) + 7*(1 + ... + #-1)).
 Both fragments will cycle 10 times, so that:
     j(final) = j(10) = j(initial) + 7*(9*10)/2 
              = j(initial) + 315.

 (But the final values of i of course differ: i(final) = 10 in the first
  fragment, and 70 in the second -- I here assumed that 'i' was a loop-
  counter and that 'j' was the variable of interest.)

 The efficiency gained in the second fragment is to replace a multiplication
 by an addition, considered "efficient" since the simplification is repeated
 ten times.

 I apologize for this verbose response, which I only entered after reading the
 first 5 and being disappointed to find no demonstration of the equivalence
 claimed for the 2 fragments. (I sincerely hope this fills that gap.)

 -- Scott McCaughrin (mccaugh@uiucmsl)