Path: utzoo!utgpu!jarvis.csri.toronto.edu!clyde.concordia.ca!mcgill-vision!bloom-beacon!snorkelwacker!think!yale!cmcl2!lanl!lambda!jlg From: jlg@lambda.UUCP (Jim Giles) Newsgroups: comp.lang.misc Subject: Re: Common subexpression optimization Message-ID: <14229@lambda.UUCP> Date: 6 Feb 90 22:20:42 GMT References: Lines: 73 From article , by pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi): > [...] > Here is the keyword: *simple*. [...] I agree. But it is clear that we have a different definition of the word "simple". Adding obfuscating detail to a program in order for the compiler to optimize it better is _NOT_ a way of simplifying the code - quite the reverse. It is sometimes (_only_ sometimes) necessary to do so in order to meet efficiency criterion for the program, but I don't have to like it. > [...] I don't like complex compilers > that second guess me. If, by "second guess", you mean compilers which change the _meaning_ of your code, then I agree! This kind of defect in a compiler is called "bad code generation" and is widely regarded as the worst defect a compiler can have. On the other hand, if you mean that you don't like compilers which generate efficient code in ways you hadn't thought of - so what? > [...] I like *simple*, obedient, compilers. The simplest compiler I know of consists of front panel switches (ie. NO compiler at all). I like mine a little more capable than that. As compiler technology improves, the set of things regarded as _simple_ will increase. This is all to the good - it means I can concentrate more on getting the code correct, portable, maintainable, etc.. It means that the frequency of occasions that I have to hand-optimize for efficiency will decrease. In short, it allows me to keep my source code _simple_. > [...] > Simplicity is the path to reliability and economy. These seem to > be lost esoteric arts :-(. I agree - see above. > [... array indexing within a loop - strength reduction ...] > In C this is done by stepping a pointer thru an array. Well, I > was assuming a more conventional language. The idea that C > pointer arithmetic is automatically scaled is another win. Consider the following two code fragments (side by side): int a[200][2]; int a[200][2]; int *p; int *q; ... ... ... p = &a; q = p+1; for (i=0;i<200;i++){ for (i=0;i<200;i++){ a[i][0] = expr1(i); *p = expr1(i); a[i][1] = expr2(i); *q = expr2(i); }; p += 2; q += 2; }; The first is clearly more readible than the second. Modern compilers can easily find the optimizations necessary to make the first run as fast as the second. The first also has the important advantage of clearly stateing something that the second obscures: there are no dependencies between the two assignments! The algorithm is stepping through two independent vectors. Compilers (and people) can easily see this on the first example and can (for example) vectorize the first more easily. The general problem of analysing dependencies in the second kind of code is VERY difficult - it's clearly and explicitly stated in the first code fragment. (I know, I made the problem harder by looping on the first index instead of the second. But the other way around won't make general dependency analysis any easier for the pointered case.) This is a case where your verbose approach actually decreases the information content of the code for _both_ the programmer and the compiler. J. Giles