That set of rules makes sense, sort of. But they're limited.
Specifically, the blanket rule "don't use modulo" is somewhat misguided, and should really mean "avoid the use of code that results in a divide operation".
(My set of rules would be to understand how the hardware works, compile to assembly and inspect the result, profile your code, and benchmark, benchmark, benchmark).
If you had a line of code that said a = b % c;
then (assuming that a, b, and c are integers) you're specifying to the compiler that c
could be any integer value. It would have to compile in a divide operation. Divide operations take lots of either time or logic area; in either case that translates to energy consumed to perform a divide.
In your specific case, where you say my_index = (my_index + 1) % 8;
then the compiler -- even with optimizations set at their lowest level -- will probably turn that into the machine language equivalent of my_index = (my_index + 1) & 0x0007;
. In other words, it won't divide (way expensive), it won't even branch (less expensive), but it'll mask (least expensive on most processors today). But this will only happen if the modulo is by a power of two.
You could insure that by just using my_index = (my_index + 1) & 0x0007;
, at the cost of code comprehension and thus code maintainability. Comment it well if you go down that road.
So in your specific case, as long as that % 8
doesn't change, or only ever changes to % N
where N
is always \$2^n\$, and the compiler knows that, the speed won't change. But if you or someone else comes along later and changes it to my_index = (my_index + 1) % 17;
(or any other non power of 2) then suddenly your code will have a divide operation and it'll be more power-hungry. In that case, using the conditional statement will be less expensive.
(In C/C++, you make sure that the compiler knows the value of a constant ahead of time by using a # define
statement, or (depending on the optimizer) declaring it const unsigned int
or (stronger, if it's C++) declaring it constexpr int
. Other compiled languages (i.e. Rust) have their own ways of making this happen.)
Note: I wouldn't be surprised if a good optimizing compiler wouldn't turn the 'if' construct into a mask -- but I wouldn't be surprised if it didn't. Ditto, a really good optimizing compiler might see the my_index = (my_index + 1) % 17;
and infer the conditional construct. I don't think I'd count on that without looking at the assembly, and I don't think I'd trust it 100% -- I might use it, but I'd put a comment in the code about crossing my fingers and hoping the compiler plays nice.
Unless you're absolutely backed against the wall for power consumption, you should also be thinking about code readability and fragility. Someone will come along later and need to understand that code, and will appreciate it if it's not a minefield full of opportunities for screwing it up. That someone may be future-you, so be nice!