hgm wrote:bob wrote:mar wrote:lucasart wrote:
How about writing this instead?
Code: Select all
if (piece >= 12) goto skip;
switch (piece) {...};
skip:
The fact that 0 <= piece <= 11 can help to optimize the code is of course not known by a C compiler, nor is it known by an assembler...
It is of course known by the compiler and it will do exactly this (except in assembly), just look at the assembly the compiler produces.
This misses the point. _I_ know I don't need to make that unnecessary test. I am 100% certain that my pieces are never out of range. So whether the compiler produces the test as part of the switch, or you do it yourself, it is STILL a wasted test. One I will NOT have in my hand-written assembler code.
One I can safely omit because I know something the compiler doesn't (can't) know, that the piece will never be outside of the range I gave for the cases...
The compiler can know that if you use an enum type for piece.
The compiler does not have to know that if you won't use a switch().
Basically what you are saying is: "if the C-code is poorly written, I can write assembly code that is better". But that is pretty obvious, and not the issue. The issue is if you can do better in assembler than your best in C.
I only consider "my best in C" vs "my best in asm". I've never found a case I could not improve on, unless you talk about completely trivial computation.
The switch case was simply an example showing what I might know that the compiler can't determine. Other examples are carrying important things in registers across procedures. Compiler can't do that since it won't know what happens down-stream when one compiles modules individually. There's a ton of good books on doing such optimizations. Compilers are very good at the common cases like recognizing common sub-expressions and the like. But then a good C programmer probably doesn't use those anyway because it looks ugly. But all the individual "tricks of the trade" simply are not included in optimizers because they affect so few programs overall, and one doesn't want a horrendously complex optimizer since they are already complex enough.
Note that this is not about "classic optimizations" which might include the above-mentioned common-subexpression elimination, strength reduction, loop unrolling, blocking (for cache) optimizations, and such. It might well include unusual things like using unexpected registers, or uncommon instruction usage, and other tricks like instruction hoisting and instruction scheduling tricks that the compiler simply doesn't try to deal with.
Compilers are certainly very good. But they are far from perfect. The issue becomes one of time. Is the speed gain from hand-coding worth the increased cost of doing the programming at a low level?
BTW ENUM doesn't help every case. Suppose _I_ know that this variable can only have white piece values at this particular point? The compiler likely won't be able to determine that, which still leaves it at a disadvantage on switch() statements.