I understand that the modulus operation can be optimised using a little & wise magic where the divisor is a power of 2...
Yes, a modulo x % pow(2, n)
can be achieved using x & ((1 << n) - 1)
But the %-operator in java can give different results if x is negative, so blindly substituting one for the other can break code.
When bit-addressing, masking etc. the &-variant is commonly used, as its semantically closer to what is used in assembler/C and signedness is usually not wanted/cared about in that case.
As for if the JIT optimizes % to &, the answer is: it depends entirely on the JIT - and thats a moving target.
In case x % y
where y isn't a constant its pretty difficult to detect if y is a power of 2, so presumably that case isn't optimizable because detecting it is very difficult or impossible. If y is a constant, the JIT still has prove that x isn't negative - or insert a conditional similar to result = x < 0 ? x % y : x & (y-1)
. It may or may not do so, depending on JIT in question and also depending on the platform. At least Hotspot is known to use different optimizations for different processors (of the same ISA, namely AMD vs Intel) in some cases.
Here is one sample code snippet
public class Test {
public static void main(String[] args) {
int a=103;
int b=5;
int c=a%b;
}
}
Now if you see the compiled code of it You will see
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 103
2: istore_1
3: iconst_5
4: istore_2
5: iload_1
6: iload_2
7: irem
8: istore_3
9: return
bipush- push a byte onto the stack as an integer value ie 103
istore_1-Pops an int off the stack and stores it in local variable,in the current frame.
and in 3: iconst_5 constant integer is pushed onto the stack
istore_2 does the same as istore_1 just variable named changed.
Then 5: iload_1
and 6:iload_2
both the variables are again pushed onto stack for operations
now at 7:irem the remainder operator works which you are calling as modulo.
Now how does Remainder operators works. It's Pops two ints(value of a and b) off the operand stack, divides a by b , computes the remainder and pushes the int remainder back onto the stack. The remainder is (b - ((a / b) * b)). This is what is used by the % operator in Java.
I spent some time on this very question, writing this blog post with all the details.
In short:
irem
(mod int) is ~20% slower than the n & (pow2-1)
trickfrem
(mod float) is 3x slowerSo, there is a clear benefit to not do mod
on Doubles, and you might get some benefit with Natural Integer Dividends and power of 2 Divisors.