Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 617
stochastic Avatar asked Jan 26 '23 03:01

stochastic


1 Answers

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.

like image 180
Acorn Avatar answered Feb 01 '23 14:02

Acorn