如何在HotSpot JVM中实现模运算符?
我知道模数运算可以使用一个小的明智的魔法来优化,其中除数是2的幂…
- 并且可能这是JIT编译器的优化?
是的,使用x & ((1 << n) - 1)
可以实现模x % pow(2, n)
x & ((1 << n) - 1)
但是如果x是负数,那么java中的%-operator可以给出不同的结果,所以盲目地用一个代替另一个可能会破坏代码。
当比特寻址,屏蔽等通常使用&变体时,因为它在语义上更接近汇编程序/ C和签名中使用的内容,在这种情况下通常不需要/关心。
至于如果JIT将%优化为&,答案是:它完全取决于JIT - 这就是一个移动的目标。
如果x % y
,其中y不是常数,如果y是2的幂,则很难检测到,所以可能这种情况不可优化,因为检测它是非常困难或不可能的。 如果y是常数,JIT仍然certificatex不是负数 - 或者插入类似于result = x < 0 ? x % y : x & (y-1)
result = x < 0 ? x % y : x & (y-1)
。 它可能会也可能不会,这取决于所讨论的JIT,也取决于平台。 至少Hotspot在某些情况下对不同的处理器(同一个ISA,即AMD与Intel)使用不同的优化。
我在这个问题上花了一些时间,写了这篇博文 ,内容包括所有细节。
简而言之:
- HotSpot JDK 1.8
irem
(mod int)比n & (pow2-1)
技巧慢约20% - HotSpot JDK 1.8
frem
(mod float)慢3倍 - 您的里程根据JIT通行证而有所不同,因此Int的优势很小
因此,不对双打进行mod
有明显的好处,你可以通过自然整数红利和2除数的力量获得一些好处。
这是一个示例代码段
public class Test { public static void main(String[] args) { int a=103; int b=5; int c=a%b; } }
现在,如果您看到它的编译代码,您将看到
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-将一个字节作为整数值(即103)推入堆栈
istore_1-从堆栈中弹出一个int并将其存储在当前帧中的局部变量中。
在3中:iconst_5常量整数被压入堆栈
istore_2与istore_1相同,只是名为changed的变量。
然后5: iload_1
和6:iload_2
这两个变量再次被压入堆栈进行操作
now at 7:irem the remainder operator works which you are calling as modulo.
现在Remainder运算符如何工作。 它在操作数堆栈中弹出两个整数(a和b的值),除以b,计算余数并将int余数推回堆栈。 余数是(b – ((a / b)* b))。 这是Java中%运算符使用的内容。