Search code examples
c++for-loopcoding-styleconditional-statements

Avoid checking the same condition every step in a loop in C++


I am checking a condition inside a loop, and if it holds, do something.

for (i = 0; i < n; i++)
{
    // do lots of work here
    .
    .
    .
    if (constant_condition)
        do_something(n);
}

The condition is independent of n, so it feels redundant to check it every time. I could instead do this:

if (constant_condition)
    for (i = 0; i < n; i++)
    {
        // do lots of work here
        .
        .
        .

        do_something(n);
    }
else
    for (i = 0; i < n; i++)
    {
        // do lots of work here
        .
        .
        .    
    }

This new code is more efficient, but I had to copy paste the same code in my program. Is there an efficient way to do this without repeating the same block of code?

Edit: the condition is not known at compile time, but it will be given at runtime and will not change.


Solution

  • First of all, profile to see if it matters. If it does, you have several options:

    • Cache the constant outside of the loop if the compiler hasn't done so already. This is the simplest and in most cases, enough:

      const bool constant_condition = ...;
      for (...) {
         ...
         if (constant_condition) do_something(...);
      }
      
    • If you really need to avoid the branch, a typical approach is to define an auxiliary function or a local lambda (C++11) to factor out the common blocks of code. However, this still duplicates code and, depending on the case, may not look pretty at all:

      auto main_work = [...](...) { ... };
      if (constant_condition)
          for (...) { main_work(...); }
      else
          for (...) { main_work(...); do_something(...); }
      
    • Define a template and parametrize as needed. The compiler will typically optimize properly, so you can simply copy-paste the code. If you really want to ensure the branch is removed, you can force it specializing the template, or taking advantage of if constexpr (C++17) etc. However, beware of code bloat and compilation times.

      template <bool constant_condition>
      void f(...) { ... }
      
      if (constant_condition)
          f<true>(...);
      else
          f<false>(...);
      

    Finally, don't forget to profile again. Sometimes, removing a branch may look good, yet be detrimental overall. This is specially true if the code changes a lot and what initially looked like a small duplication of instructions is now several memory pages full of duplicated code.

    Another alternative is trying to see if the algorithm/code can be written as branchless instead; however, that isn't a general solution.