-1

This is not the typical debate in FizzBuzz code about how to handle repeated if statement. I believe that it is a matter of personal preference.

I am here to ask about the choice of repeated mathematical operation.

Usually the solution to a FizzBuzz code is to use the modulus operator on every iteration. However, I'm concerned with its performance impact.

From my knowledge, modulus operator is tightly connected with division operator, and has a considerable performance overhead compared to simple increment and decrement operator.

In addition, I don't believe compiler has the ability to analyse and optimise the use of modulus operator in a loop.

Hence, I give the alternative solution that uses only simple decrement operator. The following code are written in C++.

I seek insight on whether my approach will provide performance benefit, or if it is actually a horrible idea that worsen performance.

And if there are other ways I can optimize a FizzBuzz code even further. (I think in a modified FizzBuzz puzzle with lots of cases to compare against, a lookup table might be preferable)


Typical FizzBuzz solution.

for (unsigned int i = 1; i <= 1000000; ++i) {
    if (!(i % 15))
        std::cout << "FizzBuzz\n";
    else if (!(i % 3))
        std::cout << "Fizz\n";
    else if (!(i % 5))
        std::cout << "Buzz\n";
    else
        std::cout << i << '\n';
}

My optimized code.

static const unsigned int fa {3};
static const unsigned int fb {5};

unsigned int a {fa};
unsigned int b {fb};

for (unsigned int i = 1; i <= 1000000; ++i) {
    --a, --b;
    if (!a && !b) {
        a = fa;
        b = fb;
        std::cout << "FizzBuzz\n";
    } else if (!a) {
        a = fa;
        std::cout << "Fizz\n";
    } else if (!b) {
        b = fb;
        std::cout << "Buzz\n";
    } else {
        std::cout << i << '\n';
    }
}
  • If you are doing FizzBuzz for a coding assessment for a job interview, I would advise against handing in your "optimized code". It looks obfuscated and difficult to maintain. – JHBonarius Jan 16 '21 at 10:19
  • And IMHO this question is more suited for [Code Review SE](https://codereview.stackexchange.com/) – JHBonarius Jan 16 '21 at 10:21
  • @JHBonarius Don't worry, this isn't for a job interview. I just came across FizzBuzz recently and it picked my interest. – Desmond Rhodes Jan 16 '21 at 10:22
  • By the way, you can always hypothesize a lot about (micro) optimizations. But the only way to know is to **_measure_**. I would advise you to learn things like [Google Test Framework](https://github.com/google/googletest) so you can for instance use [QuickBench](https://quick-bench.com/). – JHBonarius Jan 16 '21 at 10:39
  • @JHBonarius Sure, I'll look into it. Will be beneficial, but will also be taking quite some time for me. – Desmond Rhodes Jan 16 '21 at 10:51
  • 1
    Seem equivalent from [quickbench](https://quick-bench.com/q/JK7zRUZFDNMV7OsKP-RQICheZgg) (used `std::stringstream` instead of `std::cout` and lower limit to avoid time limit). – Jarod42 Jan 16 '21 at 11:00
  • @Jarod42 Thank for your example! I tried removing the IO bottleneck, and it seems like my optimized code gives a little bit of performance improvement. Result [here](https://quick-bench.com/q/KpsBSq8Yt5mo8vluEJzisZZaNYw). – Desmond Rhodes Jan 16 '21 at 11:20
  • I know that it isn't representative of usage that requires output stream, but at least it proves the hypothesis that repeated uses of modulus operator is expensive. – Desmond Rhodes Jan 16 '21 at 11:25

1 Answers1

1

In both the modulus case and the decrement case, the running time will be dominated by the stream output. Even disregarding I/O, the conditionals (which will be poorly predicted) will contribute far more than the arithmetic. Finally, integer division or modulus by a constant number is quite fast, as it is implemented through a special multiplication routine rather than with DIV.

In some cases it can be faster to replace a modulus with a conditionally set counter. This situation is not one of those.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • _the conditionals (which will be poorly predicted)_ What does this mean? And why does the conditionals contributes a lot more than the arithmetic? – Desmond Rhodes Jan 16 '21 at 10:20
  • IMHO this is more of a comment than an answer – JHBonarius Jan 16 '21 at 10:21
  • @DesmondRhodes Modern CPUs are inefficient at executing branches (such as in loops and if-statements) unless those branches follow certain extremely simple patterns. See https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array . – Sneftel Jan 16 '21 at 10:34
  • @JHBonarius No, this is a direct answer to the OP's question about whether the approach will provide a performance benefit. – Sneftel Jan 16 '21 at 10:35
  • 1
    `false, false, true` seems a simple pattern ;-) – Jarod42 Jan 16 '21 at 10:41
  • @Jarod42 That's not the pattern, though. The pattern has a period of 15. (Though even false, false, true would likely be too weird for the branch predictor.) – Sneftel Jan 16 '21 at 10:44
  • So in conclusion, branching in slow, and branch predictor won't be able to help in this case. Therefore I should worry more about a way to optimize branching before arithmetic. Noted for my future self. – Desmond Rhodes Jan 16 '21 at 10:53