What is the most efficient way to detect even numbers in Java?
If you check the assembly generated by hotspot 7 of these two methods:
public static boolean isEvenBit(int i) {
return (i & 1) == 0;
}
public static boolean isEvenMod(int i) {
return i % 2 == 0;
}
you will see that although the mod is optimised and basically does a bitwise and
but it has a few extra instructions because the two operations are not strictly equivalent*. Other JVMs might optimise it differently. The assembly is posted below for reference.
I also ran a micro benchmark which confirms our observation: isEventBit is marginally faster (but both run in about 2 nanoseconds so probably won't have much of an inmpact on a typical program as a whole):
Benchmark Mode Samples Score Error Units
c.a.p.SO16969220.isEvenBit avgt 10 1.869 ± 0.069 ns/op
c.a.p.SO16969220.isEvenMod avgt 10 2.554 ± 0.142 ns/op
isEvenBit
# {method} 'isEvenBit' '(I)Z' in 'javaapplication4/Test1'
# parm0: rdx = int
# [sp+0x20] (sp of caller)
0x00000000026c2580: sub rsp,0x18
0x00000000026c2587: mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry
; - javaapplication4.Test1::isEvenBit@-1 (line 66)
0x00000000026c258c: and edx,0x1
0x00000000026c258f: mov eax,edx
0x00000000026c2591: xor eax,0x1 ;*ireturn
; - javaapplication4.Test1::isEvenBit@11 (line 66)
0x00000000026c2594: add rsp,0x10
0x00000000026c2598: pop rbp
0x00000000026c2599: test DWORD PTR [rip+0xfffffffffdb6da61],eax # 0x0000000000230000
; {poll_return}
0x00000000026c259f: ret
isEvenMod
# {method} 'isEvenMod' '(I)Z' in 'javaapplication4/Test1'
# parm0: rdx = int
# [sp+0x20] (sp of caller)
0x00000000026c2780: sub rsp,0x18
0x00000000026c2787: mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry
; - javaapplication4.Test1::isEvenMod@-1 (line 63)
0x00000000026c278c: mov r10d,edx
0x00000000026c278f: and r10d,0x1 ;*irem
; - javaapplication4.Test1::isEvenMod@2 (line 63)
0x00000000026c2793: mov r11d,r10d
0x00000000026c2796: neg r11d
0x00000000026c2799: test edx,edx
0x00000000026c279b: cmovl r10d,r11d
0x00000000026c279f: test r10d,r10d
0x00000000026c27a2: setne al
0x00000000026c27a5: movzx eax,al
0x00000000026c27a8: xor eax,0x1 ;*ireturn
; - javaapplication4.Test1::isEvenMod@11 (line 63)
0x00000000026c27ab: add rsp,0x10
0x00000000026c27af: pop rbp
0x00000000026c27b0: test DWORD PTR [rip+0xfffffffffdb6d84a],eax # 0x0000000000230000
; {poll_return}
0x00000000026c27b6: ret
* as pointed out in the comments, %
isn't really modulo; it's the remainder. So (i % 2) != (i & 1)
if i < 0
. The extra instructions in the isEvenMod
code sets the sign of the result to the sign of i
(and then just compares it to zero, so the effort is wasted).
Another approach is to run a micro benchmark and analyse the time taken by each variants. Here are the results:
Benchmark Mean Units Time vs. baseline
baseline 10.330 nsec/op 0.000
bitAnd 12.075 nsec/op 1.745
bitShift 12.309 nsec/op 1.979
modulo 12.309 nsec/op 4.529
(the baseline is a method that just returns i == 0
)
Conclusion:
i & 1
-----> takes about 1.75nsi << 31
--> takes about 2.00nsi % 2
-----> takes about 4.50ns
In other words, i % 2
is 2x slower than i & 1
.
Notes: benchmark done with jmh. The baseline is high because I generate random numbers to make sure the method are not optimised away. Tests run on an i7 @ 2.8GHz (i.e. one cycle = 0.35ns) with hotspot 7.