java i++和++i的区别_漫画:奇怪,为什么在Java中 2*(i*i) 比 2*i*i 快?

5a7cc2fbb1cfea581f657b9c36e57a48.png

今天这篇文章,我们通过一个故事来深入聊聊 Java 编译背后的秘密。

78adeca7ff3f0baea254abb8ccbbce06.png

8f3d3979d5b217f50ea8f80893b2694a.png

8540e3593437e85c4792010ad79e73de.png

ebf1f5aaebf5d3d6d47823bdbc8d8661.png

748d7db8d9a6e3dbfa2f5e3cb0ecb591.png


东哥说这段代码来自于 Stackoverflow(关于这个网站,东哥似乎分享了无数次《这三个网站的使用技巧,你值得收藏》,真正去逛这个网站的同学还是很少),如下。
public static void main(String[] args) {
long startTime = System.nanoTime();
int n = 0;
for (int i = 0; i < 1000000000; i++) {
n += 2 * i * i;
}
System.out.println((double) (System.nanoTime() - startTime) / 1000000000 + " s");
System.out.println("n = " + n);
}
代码很简单吧,它的执行时间大概在 0.60s ~ 0.65s。但是如果你把 2*i*i 替换成 2*(i*i),执行时间大概在 0.50s ~ 0.55s。
对这段程序的两个版本分别执行 15 次,得到的结果如下。

8ee8e06392793db12ca32ab638cc1a73.png


我们可以看出 2*(i*i) 比 2*i*i 快 。

81c0a62da677eb8b110175181ce32381.png


我们来分别查看它的字节码,这里东哥给我推荐了一款好用的 IDEA 插件,叫做 jclasslib bytecode viewer(你还不会用的话,可以去查看一下东哥一系列的 IDEA 文章《用 IDEA 跟踪 Java 源码的技巧 | 内部资料》,真心不错)。
2*i*i 的字节码如下。

05e46768f2a8f0fd0b5c710894875f69.png


2*(i*i) 的字节码如下。

630eec9facb570cb8449e3ff3261bd90.png


我们可以发现除了字节码顺序不同外,没有其它异常,下一步该怎么办呢?

2978ff91e428bd73cf37de935c771692.png


这里东哥给我推荐一款开源的工具,叫做 jitwatch,它可以查看查看分析HotSpot JIT compiler 生成的汇编代码,关于它的安装方法可以通过谷歌查阅。
查看是否安装成功?可以用下面的命令。

89d59115d500a74f055ec3a6f77593a0.png


如何查看汇编代码?
运行时,添加参数 -XX:+PrintAssembly。
好了,那我们来分别看看它们的汇编代码,由于汇编代码很多,我这里就不贴出来了,各位同学可以去运行分析一下。
我这里就说一下结论,通过对比分析,我们会发现,2*i*i 进行了大量的堆栈操作,因此,需要保存大量的中间结果;而 2*(i*i) 只有少量的堆栈操作。
显而易见,2*(i*i) 比 2*i*i 快是由于 JIT 优化的结果。

19de111e00c05aaed2b1c26f28323d00.png

说明:
本文资料来自于Stackoverflow、Github。东哥的第一篇漫画文章,如果喜欢的话,转发支持一下,给东哥继续创作下去的动力。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值