chasep255
chasep255

Reputation: 12185

Is it faster to use bit operators or if statements in C++?

I was reading somewhere that it is faster to use bitwise operators instead of if statements where possible. I am working on an image processing project and I have various methods for doing math on pixels. For instance when I add a pixel I would check and make sure the sum does not go over the maximum value. I changed it to be this...

    Pixel16 operator+(Pixel16 p) const noexcept
    {
        uint_fast32_t r = red + p.red;
        uint_fast32_t g = green + p.green;
        uint_fast32_t b = blue + p.blue;
        return Pixel16(r | -(r > 0xffff), g | -(g > 0xffff), b | -(b > 0xffff));
    }

Do you guys think it is faster than writing statements like...

if(r > 0xffff)
 r = 0xffff;

FYI red, green, and blue are member variables of type uint16_t

Upvotes: 1

Views: 2800

Answers (4)

gnasher729
gnasher729

Reputation: 52632

This is what used to be called a microoptimisation thirty years ago, because it could save microseconds. Today I'd call it a nanooptimisation :-)

Unless you have reason to believe that the speed is affecting anyone, do what is more readable.

If speed is important, for example because you need to process 60 10 megapixel images per second, then you try out various methods and measure the execution time. The exact speed will depend on your compiler, on the processor, sometimes on pure good luck or bad luck. Obviously you use the same compiler optimisations settings that you use in production code.

And which code produces the fastest results may be different on different processors. So you leave all variants in your source code (including the most readable one), and use #ifdef to pick the known fastest one where you have measured it. Say your code is supposed to run on three current and five future devices. And you measured on two of the current devices. Then you make sure that on these two devices the fastest measured code is run. On the other devices it's a judgement call. If A was 40% faster than B on processor X, but 5% slower on processor Y, then you'd probably use A on all processors where the speed wasn't measured. And you add the measurement results as comments.

Now all that is a lot of work, so you only do it if the time saved is worth that effort.

Upvotes: 1

Bill Lynch
Bill Lynch

Reputation: 82026

Given this code:

#include <algorithm>
#include <cstdint>

struct Pixel16 {
    uint16_t red;
    uint16_t blue;
    uint16_t green;

    Pixel16(uint16_t red, uint16_t green, uint16_t blue);

};

Pixel16 v1(Pixel16 const & p, Pixel16 const & s) {
    uint_fast32_t r = p.red   + s.red;
    uint_fast32_t g = p.green + s.green;
    uint_fast32_t b = p.blue  + s.blue;
    return Pixel16(r | -(r > 0xffff), g | -(g > 0xffff), b | -(b > 0xffff));
}

Pixel16 v2(Pixel16 const & p, Pixel16 const & s) {
    uint_fast32_t r = p.red   + s.red;
    uint_fast32_t g = p.green + s.green;
    uint_fast32_t b = p.blue  + s.blue;

    r = std::min(r, (uint_fast32_t) 0xFFFF);
    g = std::min(g, (uint_fast32_t) 0xFFFF);
    b = std::min(b, (uint_fast32_t) 0xFFFF);

    return Pixel16(r, g, b);
}

What does my compiler give as a result?

Clang on OS X will generate functionally identical code for v1 and v2. The only difference is that the order that the calls to min() and the equivalent work in v1 occur in a different order.

In both cases, there are no branches.

Summary:

Write the code that is most understandable. Use functions and language features to express your code in a readable manner. Is your bitwise code more or less readable than a min() function?

Upvotes: 2

Adrian McCarthy
Adrian McCarthy

Reputation: 48038

To know, you have to measure (profile the code).

The theory as to why the bitwise operations might be faster is that branch prediction failures can be a significant performance hit. By using bitwise operations instead of control flow switches, you may be able to avoid a conditional branch altogether and thus you'll never have a branch prediction miss there.

Depending on the situation, it might also be slightly cache-friendlier to compute rather than branch.

But you won't know the actual effects unless you measure. Too many factors are in play to simply analyze the code.

Upvotes: 1

Joe Sewell
Joe Sewell

Reputation: 306

In almost all cases "faster" will depend greatly on the target system and the compiler used, as well as how operators may be overloaded. In this case, though, I seriously doubt there will be that much of a difference, since you're still using comparison operators, which should be the more expensive operation than a simple if branch.

The code listed above, though, bothers me. Negating the result of a comparison operation (a boolean operation, unless you've overridden the operators) is not something that's safe to bitwise-or like you're doing. Besides, it's very difficult to understand, which means it'll be very difficult for someone to maintain later on. The if version, though, explains what's going on.

Upvotes: 1

Related Questions