Efficiency of multiple If conditions

A doubt that popped up while I was writing some simple code:

Is worst-case number of operations in Code 1 fewer than Code 2 below?

In Code1, if the 1st condition fails after reading Variable1, I assume the microcontroller won't even do any memory-read of Variable2 as it proceeds to skip the 2nd condition test. Whereas in Code2, since both conditions are within the same line of code, are they both treated as one unit and thus would the microcontroller do memory-reads together of both Variable1 and Variable2 right off the bat? Or would Code2 also skip the whole line as soon as the 1st condition fails?

Code 1:

if (Variable1 == ConstantX) {
 if (Variable2 == ConstantY) {
  DoAction();
 }
}

versus Code 2:

if (Variable1 == ConstantX && Variable2 == ConstantY) DoAction();

Or would Code2 also skip the whole line as soon as the 1st condition fails?

This is what C does. Efficient, but can be a little surprising.

Efficient, but can be a little surprising.

Especially if the if test involves a function call, and you expect that function call to be made.

You can do what Nick Gammon frequently does - look at the lower level code and see what actually gets created by the compiler.

wildbill:

Or would Code2 also skip the whole line as soon as the 1st condition fails?

This is what C does. Efficient, but can be a little surprising.

PaulS:

Efficient, but can be a little surprising.

Especially if the if test involves a function call, and you expect that function call to be made.

Interesting. So in general, for such functions, it's probably better to store the function-return-value to a variable first (although that comes at the cost of one extra variable) and only then do any test that involves multiple conditions.

CrossRoads: You can do what Nick Gammon frequently does - look at the lower level code and see what actually gets created by the compiler.

Robert, how does one look at the lower level code (I presume you mean the code behind the scenes of the Arduino C code)?

Creating a temporary to store a function return would defeat the purpose of optimizing the if statement.

You should read up on operator precedence and associativity. It explains how the operators are evaluated allowing you to design your code.

operator precedence is nessecary to understand things like

i +++ j;

or

myarray[ myval ] = myval++;

Creating a temporary to store a function return would defeat the purpose of optimizing the if statement.

Nowadays compilers can also optimize that too by holding the value in a register. Same as would be done if the function call would be in an if. It depends mainly if the returnvalue of the function is used thereafter. Good practice declare the variable just before the if statement - do not use a global var for it.

You can test this quite easily if the Arduino can optimize such a "temporary variable" by comparing the assembly code (check the command objdump.exe) or look at the size of the compiled code. NB if it does not optimize the extra assignement will take a few bytes more.

In my opinion code should be written to be easily read for humans.
Unless everyone understands the proper precedence I would thus implement it depending on the intent:

  1. If functions are always to be executed I would store the results in intermediate variables and then evaluate the variables in the if statement.

  2. If functions are to be executed only if necessary I would nest if statements.

  3. If it does not matter I would evaluate the functions inside the if statement.

In all 3 cases I would not mind to much about the compiler. Usually compiler optimizers are pretty clever. If performance is an issue I would recommend to profile and then to optimize. The other way round often lends to optimization in the wrong places.

I don’t know how to obtain the assembly code. Haven’t written anything that big that I needed to worry about it yet.

CrossRoads: You can do what Nick Gammon frequently does - look at the lower level code and see what actually gets created by the compiler.

Yes I do that mainly from curiosity, particularly when I suspect the compiler has optimised code away completely.

As a pretty good rule of thumb, you won't beat the compiler in its ability to optimise. For one thing it will put things in temporary registers, and do other fancy things. Trying to be smart can actually defeat the compiler's ability to work out what you are trying to do.

My notes for getting the assembly code are:

  1. Hold down SHIFT key and then click on the "verify" button (LH one) to compile your sketch in verbose mode.

  2. Highlight and copy to the clipboard the full pathname of the generated .elf file.

  3. In a terminal window do something like:

avr-objdump -S xxxx.elf

*** For .hex files:

avr-objdump -j .sec1 -d -m avr5 xxxx.hex

The xxxx.elf file you want is usually about the third last line (or so) of the listing you see in verbose mode.

Then I usually search for some keyword I expect to see (like "setup") to narrow down where to look.

I also usually "pipe" the results into a file, like this:

avr-objdump -S xxxx.elf > ~/nick.txt
open ~/nick.txt

Then use the usual searching functions in the text editor.

CrossRoads: I don't know how to obtain the assembly code. Haven't written anything that big that I needed to worry about it yet.

I agree. Size isn't the criteria. Maybe you are trying to see how many clock cycles are likely to be used in an ISR. But then you can work that out by measuring anyway.

You shouldn't really be in the situation where a clock cycle or two is critical, because every now and then an interrupt will go off and throw out your timing.

[quote author=Udo Klein link=topic=77825.msg588543#msg588543 date=1320493526] In my opinion code should be written to be easily read for humans. [/quote]

Absolutely. In six months time you will forget the ingenious ideas you had in your head, so write easily-understandable code. If timing is highly critical, and the code has to be obscure, explain heavily in the comments what and why you are doing something.