I got the impression that if else if chains seem to often produce smaller code then switch statements. Has anyone an explanation why this is the case? I as somewhat curious. I would have expected that switch statements produce shorter code. Why is it the other way round?
One way to optimize switch statements is to use a jump table. The typical sequence is to perform a bit of math on the switch expression and then perform an indirect jump using the table.
I have no idea if the compiler performs this optimization but, if it does, that might explain the difference in code size.
Interesting observation, and something to keep in mind if you are needing to save a few precious bytes on a large program. I don't know why it would be, but I think as a developer I would rather see people use switch-case, than if-then-else, unless the situation demanded it because of being tight on space. The latter being less readable than the former, and a potential maintenance issue down the road (unless, as noted, its use can be justified - in which case the reasoning should be put in the comments, and plenty of comments in the code to help someone who isn't the original developer follow along).
@cr0sh: this is exactly why I converted most of my stuff to switch instead of if then else. However I wonder why this made my programs longer. Not that I am running out of space. I use a 644 and have still ~8k left. But I am still wondering.
It may be, as Coding Badly suggests, that switch-case gets compiled as a lookup table with jump offsets. That could explain the larger code, but I would much rather see this fixed in the code generator, than having to rewrite my code.
The comment below is from the Arduino bootloader source.
/* A bunch of if...else if... gives smaller code than switch...case ! */
Then what is the nature of the larger code? Is it a fixed overhead for every switch-case statement or is a shared library segment used to handle the lookups? Is the difference significant for a small, large (how large) number of case entries?
The compiler is extremely good in optimizing things, understanding assingment of integers and their consequences, removing unused code. This works most likely not so well with the more systematic "cases".. Even without optimization, switches will have a small overhead, that will level out for 4 or more cases only. More cases should give shorter code..
I created a small benchmark comparing switch-case to if-else-if that revealed the following (using non sequential, single byte conditions):
1. Using 4 branches and a single set of if/switch constructions:
Size: If is smaller by 24 bytes
Time: If is faster by 25%
2. Using 8 branches and a single set of if/switch constructions:
Size: If is smaller by 16 bytes
Time: Switch is faster by 24%
3. Using 4 branches and a double set of if/switch constructions:
Size: If is smaller by 42 bytes
Time: If is faster by 27%
4. Using 8 branches and a double set of if/switch constructions:
Size: If is smaller by 30 bytes
Time: Switch is faster by 27%
5. Using 5 branches and a single set of if/switch constructions:
Size: If is smaller by 10 bytes
Time: Switch is faster by 17%
Generally I would prefer switch-case for anything with 4 or more branches for readability. Apparently this is neither fastest nor smallest on the Arduino.
From 5 branches onwards, switch is faster, however at a small code size penalty.
so most of the tests are on values that does not match any branch of the ifs or switches. In a more realistic scenario, I'd say few values would be unmatched, and the if's would be coded with the most likely value first.
Running the modified version (using your set of 8 values) gave the following (using one set of 8 branches):
"If" drops from 25 to 17 ms and "Switch" goes up from 19 to 22 ms. So effectively this changes the benchmark in favor of "If" also in terms of speed.
What triggered my interest in this was the comment I had seen in the bootloader source and then the post on this topic. Apparently there is some benefit to using "if-else" (both speed and size), but then again there are a number of different optimizer levels and switches that are likely to be much more significant and possibly can they also skew this simple benchmark somewhat.