3

I've a conditional statement expensive_foo() which is false in 99.9% cases. And I have a conditional statement bar which is true in ~50% cases.

And I want some action be done if both statements are true. So I almost certainly know that expensive_foo() is false and I want to check it only if bar is true.

Will the code below check the expensive_foo() ONLY if bar is true? Or it will check expensive_foo() every time?

if ( bar && expensive_foo() )
{
    ...
}

Or I need to make a structure like this:

if ( bar )
{
    if ( expensive_foo() )
    {
        ...
    }
}
sehe
  • 374,641
  • 47
  • 450
  • 633
Kolyunya
  • 5,973
  • 7
  • 46
  • 81

5 Answers5

5

The logical AND operator && is short-circuited, which means that it is guaranteed the second operand is evaluated if and only if the first one is evaluated as true. The conditions if (bar && foo) and if (bar) if (foo) are identical.

Given that foo is expensive to compute, you should almost definitely check bar first. Even though bar won't be predictable by the branch predictor very well, that's a minor effect compared to the computation of foo.

Thus the structure should probably be:

if (bar)
{
    if (bool const foo = compute())   // expensive
    {
        // ...
    }
}

Note that all of those constructions mean that we may or may not call compute(). If you require the function call unconditionally, then you should make it the first check. In that case, the successful branch prediction on the result should be exploited.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Which is only true semantically, not performance wise. If bar and foo are not side-effecting the compiler can still reorder them causing a performance penalty. – usr Sep 14 '12 at 12:51
  • @usr: I'm pretty sure it can't. – Puppy Sep 14 '12 at 13:04
  • @DeadMG the compiler can do anything which does not change the behavior of the program according to the standard. If no behavioral change is observable the compiler can do this. (The OP edited the post though telling us that foo is actually foo_expensive(). This makes my point moot, although it was correct). – usr Sep 14 '12 at 13:05
  • @DeadMG depends. If they have side-effects the compiler can't reorder the side-effects. Otherwise, you can't observe the difference. – R. Martinho Fernandes Sep 14 '12 at 13:11
  • @Kolyunya: No. `if (bar && compute())` is identical to `if (bar) { if (compute()) { /* .. */ } }`. It's safe and fine. I just thought you might like to store the result of `compute()`, or have an `else` branch. But if you don't, feel free to put everything into one single condition. – Kerrek SB Sep 14 '12 at 13:11
  • @KerrekSB thanks! And it seems to me, you've made a mistake: not `false` but `true` in the first sentence. – Kolyunya Sep 14 '12 at 13:18
  • @Kolyunya: Err... yes! Argh. I'm not with it today. It's the opposite for `||` and for `&&`. Whichever one makes sense! :-) – Kerrek SB Sep 14 '12 at 13:20
  • Some processors can conditionally execute instructions. So the impact to the execution would be negligible for simple conditionals. – Thomas Matthews Sep 15 '12 at 00:46
2

Both codes will check foo only in case bar is true. In first case this is guaranteed by the language lazy boolean expression evaluation (which actually could be explicitly turned off in some compilers, but is ON by default).

UnknownGosu
  • 854
  • 6
  • 9
1

The other answers are good, but I'm questioning the premise.

If you are querying a predicate that is false 99.9% of the time, that is analogous to doing linear search in a list of 2000 entries, where the item you are looking for could be anywhere with roughly equal probability.

In that case, typically one would try to organize the list differently so as to do a O(logN) (or better) search instead of O(N). I don't know if you can do this, but that is where I would focus.

When a question is being asked with highly skewed probability, that can be a big speedup opportunity, if one can un-skew it. (Assuming a big % of time is spent in this activity. If not, the point is moot.)

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
1

I'd be surprised if the optimized assembly for both wasn't exactly the same. expensive_foo() will never be called if bar is false.

mark
  • 5,269
  • 2
  • 21
  • 34
1

I saw a comment that the compiler can re-order operations and I'm inclined to that opinion. The efforts to improve automatic parallelization of the sequential programs has resulted in somewhat better compilers and it helps a programmer to avoid complex routines of parallelization from their programs. The compiler could re-order the operations provided the re-ordering does not violate/worsen the locality or in a broad sense doesn't violate the actual intention of the of the program.

But I doubt the fact that the compiler would try to re-order a conditional statement like :

  if ( bar && expensive_foo() )

If the less expensive Boolean function is put at the beginning, it is certain that we can save some time.

sjsam
  • 21,411
  • 5
  • 55
  • 102