如何在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_16: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中%运算符使用的内容。