Question

In the following for-loop, I don't do anything if conditionA, conditionB, and conditionC all evaluate to true.

for (int i = 0; i < imax; ++i) {
    bool conditionA;
    // evaluate conditionA

    bool conditionB;
    // evaluate conditionB

    bool conditionC;
    // evaluate conditionC

    if (conditionA && conditionB && conditionC) continue;

    // do something
}

If conditonA evaluates to false, it becomes unnecessary to evaluate conditionB and conditionC. So it seems that I can speed up the loop by writing it in the following way.

for (int i = 0; i < imax; ++i) {
    bool conditionA;
    // evaluate conditionA

    if (conditionA) {
        bool conditionB;
        // evaluate conditionB

        if (conditionB) {
            bool conditionC;
            // evaluate conditionC

            if (conditionC) continue;
        }
    }   

    // do something
}

Now this looks ugly and is not understood at first glance. It seems that using the infamous goto the code becomes much more elegant:

for (int i = 0; i < imax; ++i) {
    bool conditionA;
    // evaluate conditionA
    if (!conditionA) goto doSomething;

    bool conditionB;
    // evaluate conditionB
    if (!conditionB) goto doSomething;

    bool conditionC;
    // evaluate conditionC
    if (conditionC) continue;

    doSomething:

    // do something
}

Does this work as a method for speeding up the loop or is the compiler smart enough that the first version of the code is actually as fast as the second and third version? In case it isn't, is there a better alternative than using goto?

Was it helpful?

Solution

I would move the evaluation of the conditions into separate functions and then do:

for (int i = 0; i < imax; ++i) {
    if (conditionA() && conditionB() && conditionC()) continue;

    // do something
}

If conditionA returns false, conditionB will never be called, and so on.

This will also make your function shorter and more concise, dividing responsibilities up among other functions.

If you have no good reason for doing an "early exit" like that, you can avoid using continue altogether:

for (int i = 0; i < imax; ++i) {
    if (!(conditionA() && conditionB() && conditionC())) {
      // do something
    }
}

Or use De Morgan's law to get !conditionA() || !conditionB() || !conditionC - whichever you prefer.

OTHER TIPS

Before you try to speed something up, consult your profiler if the loop is really the bottleneck. If it is not, leave the code readable and maintainable (as opposed to a maybe slightly faster, maybe slower but surely errorprone and unreadable mess) and leave it to your compiler's capability to speed things up.

If the loop is the bottleneck, try whatever comes to mind, profile it, and compare the results. Nobody can surely say what your compiler might optimize away.

Never trust anyone but your profiler when it comes to micro performance optimizations. Compilers are different in what they optimize and how they optimize it, and humans are particularly bad in predicting the savings of those optimizations.

However, if the conditions are not too complicated, I bet the compiler will optimize the boolean variables away anyways and leave you with something like

for (int i = 0; i < imax; ++i) {
  if(evalConditionA() && evalConditionB() && evalConditionC())
    continue;
  doSomething:
}

What you always can do is asess the probabilities to any of the conditions to become false and put the one first that is most likely to trigger short circuit evaluation, so the others wont need to be evaluated so often.

well, as always, if you're wondering about performances, just do a benchmark. Otherwise, I think the best solution really depends on your context. For example your first solution is the best if you can make your conditions in the valuation or as separate functions (or worst case as macros):

for (int i = 0; i < imax; ++i) {
    if (! (evaluate conditionA) && (evaluate conditionB) && (evaluate conditionC)) {
        // do something
    }
}

then lazy evaluation will come to help, and depending on what you do, your compiler may be able to optimize your evaluation with some optimization options.

I also advice you not to use if (condition) continue; but instead if (!condition) { /* do something */ } which helps a better understanding of the algorithm. Don't forget that what you code will be read by someone one day, and that someone may be you!

C++ Compilers (at least Visual Studio) already done what you looking for.

if( bCondA && bCondB && bCondC )

If bCondA == false, the others are not verified. Should be true for all compilers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top