0

I was reading https://iq.opengenus.org/cpp-likely-and-unlikely-attributes/ and I don't understand/agree with few things.

  1. In the following code:

    void doModulus( vector<int> &vec , int mod ){
    
      // here the value of mod we are passing is 224, vec is of size 1024 holding values in [1,255]
    
      for( int i = 0 ; i<vec.size() ; i++ )
      {
          if( vec[i] >= mod ) [[unlikely]]{
              // so we are prioritizing  else statement here
              vec[i] = vec[i] % mod ;
    
          }else {
    
              vec[i] = vec[i] ;                         
          }
      }
    }
    

does it matter or make any difference if we make the else code [[likely]] or this happens automatically in background as if one side is likely then the other side isn't? (Although I would like it to be that if the one side is unlikely then the other side (if it was likely) to be strongly likely.

  1. The following claim was made:

% operation is a computationally heavy operation for cpu , so let's try to optimize our code.

While the "optimize" refers to adding the if else conditions, but I tested the code using chrono and it seems execution time was 2-3 times faster without if-else...

Plus I don't see why % is heavy, it's just about reading bottom bits of number.

Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79
Algo
  • 23
  • 1
  • 2
    Modulo is the remainder of a division which is algorithmically non-trivial. How fast or slow it is depends on the platform. But branches have their own cost associated. If modulo is too slow, and you only care about inspecting certain bits, consider using bitwise-and to mask those bits instead which is a simple operation which should be one of the fastest operations on most architectures. Either way, I wouldn't worry about it unless the performance is problematic. – François Andrieux Nov 05 '21 at 18:08
  • 4
    The remainder from dividing by 224 is _not_ "just about reading bottom bits of number". There are no bottom bits that will indicate the result of `% 224`. – Drew Dormann Nov 05 '21 at 18:10
  • @DrewDormann %2 is most right bit, %4 most right 2 bits... – Algo Nov 05 '21 at 18:16
  • Can someone answer 1 too? – Algo Nov 05 '21 at 18:16
  • 1
    _While the "optimize" refers to adding the if else conditions, but I tested the code using chrono and it seems execution time was 2-3 times faster without if-else..._ honestly it is more interesting that the rest of your question, but it is not answerable without Minimum Reproducible Example; still my wild guess is that you may want to look at this: https://stackoverflow.com/a/11227902/2945027 – Alex Guteniev Nov 05 '21 at 18:28
  • 1
    @AlexGuteniev: On some CPUs, it can be worth branching to avoid hardware division, even if it mispredicts a lot, like 25% or maybe even 50%. On the latest Intel CPUs (Ice Lake etc.) with higher throughput integer dividers, the break-even mispredict rate is probably lower. Probably not worth it just to avoid a multiplicative inverse (modulo by a constant), even when it's signed `int` that requires extra work, unless the pattern is quite predictable. – Peter Cordes Nov 06 '21 at 02:27

1 Answers1

3

does it matter or make any difference if we make the else code [[likely]] or this happens automatically in background as if one side is likely then the other side isn't?

The C++ Standard say about likely just recommended practice and example. See [dcl.attr.likelihood]. So it is up to the implementation how they are actually used.

Though recommended practice and example imply that only one mark is needed, and compilers where the difference is observed seem to imply this as well.

Plus I don't see why % is heavy, it's just about reading bottom bits of number.

Only for a power of two. But it should be known that is is power of two in compile time. There's also a way to make this operation cheaper for a known not a power of two constant (Why does GCC use multiplication by a strange number in implementing integer division?).

For a runtime value it is heavy, it is division.

It might happen that a value is known to the optimizer due to constant propagation though. Then optimizations apply.

Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79