I wanted to confirm that the modulo operation was an expensive operation, so I tested this part of the code that checks if the given number is valid:
bool is_even(int n) { return (n & 1) == 0; }
then this one:
bool is_even_bis(int n) { return (n % 2) == 0; }
At first I used C #, and indeed, code using logical & faster than the other, sometimes even three times faster. Using ILSpy, I saw that when compiling to MSIL there was no optimization, the code is exactly the same.
However, as my friend in C noted, using gcc -O3 , the code compiles to:
is_even: mov eax, DWORD PTR [esp+4]
and
is_even_bis: mov eax, DWORD PTR [esp+4]
So basically the exact same thing. Even when using the -O0 optimization -O0 operation does not even appear:
is_even: push ebp # mov ebp, esp #, mov eax, DWORD PTR [ebp+8] # tmp63, n and eax, 1 # D.1837, test eax, eax # D.1837 sete al #, D.1838 movzx eax, al # D.1836, D.1838 pop ebp # ret
Needless to say, the compiled code matches between is_even and is_even_bis in -O0 .
Even more funny, if you can say, another friend of mine tried to use OCaml:
let is_even x = ((x land 1) == 0) let _ = let i = ref 100000000 in while !i > 0 do ignore (is_even !i); decr i done
and
let is_even_bis x = ((x mod 2) == 0) let _ = let i = ref 100000000 in while !i > 0 do ignore (is_even_bis !i); decr i done
And it looks like the modulo version works faster with bytecode, but slower in native code! Can someone explain this mystery?
Then I began to wonder why it does not behave as it does in C # (where there is an obvious performance gap between the two functions) and why the JIT compiler does not apply the same optimization as gcc . I do not know if there is a way to intercept the output of the JIT compiler, maybe this will help to understand?
Bonus question: I believe that modulo is based on division, and since division is performed in O (n²) time (n is the number of digits), can we say that the module has quadratic time complexity?