java 模运算优化_JAVA对于乘法除法和模运算的优化,是否需要转换成位运算

最近思考一个问题。我们知道,在底层汇编代码中,除以2的指令效率远低于直接右移1位。所以我看到的不止一个java教学视频(原谅我看了很多民间流传的教学视频,简单粗暴)说过/2尽量写成>>1。但是另一方面,我记得上课学过编译器的优化问题,很多事情其实是不需要程序员考虑的。那么事实是怎么样的呢?

这就要考虑到java编译的流程了:.java文件先转换成.class文件(字节码),在运行的时候,JVM先接收到字节码,再做JIT即时优化和编译,形成对应的机器码。

首先我想到的是,先查看一下.class文件的字节码,看看是否进行了优化:

1

javap -verbose 文件名 //这条指令可以反编译.calss文件,查看到具体指令。

最终看到的结果,除法被编译成了idiv,右移被编译成了ishr。就是说并没有优化。(如果是常数运算会直接在编译字节码时常量折叠,此处就不考虑了)

代码的测试结果也印证了这一点:

1

2

3

4

5

6

long a1=System.currentTimeMillis();

for (long i = 0; i < 999999999l; i++) {

j=i>>1;//或j=i/2;

}

long a2=System.currentTimeMillis();

System.out.println(a2-a1);

运算结果:

j=i/2; 1070ms

j=i>>1; 702ms

那么说到这里,为什么JVM没有把除法直接优化成右移呢,因为对于负数来说,右移不等于/2。举个例子:

-5 / 2 = -2

-5 >> 1 = -3

-5 >>> 1 = 2147483645

变量在编译期间如果再加一次正负数判断,往往是得不偿失的。因此:

对于乘法和以及%运算,JVM一定会优化,这些是不需要程序员去考虑的,直接去用*/%即可。

对于除法,因为上述问题,确实是位移更快些。

最后引申一下,虽然我们要“充分相信编译器”,但有些时候右移可能是最佳选择,例如java.util.Arrays.binarySearch:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

int low = fromIndex;

int high = toIndex - 1;

while (low <= high) {

int mid = (low + high) >>> 1;

int midVal = a[mid];

if (midVal < key)

low = mid + 1;

else if (midVal > key)

high = mid - 1;

else

return mid; // key found

}

return -(low + 1); // key not found.

这里用(low + high) >>> 1代替(low + high) /2是非常正确的,首先是因为数组下标肯定不会是负数,另一方面如果low + high大于int最大值(溢出变为负数了)时,只有>>>1能保证结果正确。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值