文章目录

  • 简介
  • 循环展开和粗化锁
  • 分析Assembly日志
  • 禁止Loop unrolling
  • 总结

简介

之前在讲JIT的时候,有提到在编译过程中的两种优化循环展开和粗化锁,今天我们和小师妹一起从Assembly的角度来验证一下这两种编译优化方法,快来看看吧。

循环展开和粗化锁

小师妹:F师兄,上次你讲到在JIT编译的过程中会进行一些编译上面的优化,其中就有循环展开和粗化锁。我对这两种优化方式很感兴趣,能不能展开讲解一下呢?

当然可以,我们先来回顾一下什么是循环展开。

更多精彩内容且看:

  • 区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
  • Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
  • Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
  • java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程

循环展开就是说,像下面的循环遍历的例子:

for (int i = 0; i < 1000; i++) {x += 0x51;}

因为每次循环都需要做跳转操作,所以为了提升效率,上面的代码其实可以被优化为下面的:

for (int i = 0; i < 250; i++) {x += 0x144; //0x51 * 4}

注意上面我们使用的是16进制数字,至于为什么要使用16进制呢?这是为了方便我们在后面的assembly代码中快速找到他们。

好了,我们再在 x += 0x51 的外面加一层synchronized锁,看一下synchronized锁会不会随着loop unrolling展开的同时被粗化。

for (int i = 0; i < 1000; i++) {synchronized (this) {x += 0x51;}}

万事具备,只欠我们的运行代码了,这里我们还是使用JMH来执行。

相关代码如下:

@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(value = 1,jvmArgsPrepend = {"-XX:-UseBiasedLocking","-XX:CompileCommand=print,com.flydean.LockOptimization::test"
})
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class LockOptimization {int x;@Benchmark@CompilerControl(CompilerControl.Mode.DONT_INLINE)public void test() {for (int i = 0; i < 1000; i++) {synchronized (this) {x += 0x51;}}}public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(LockOptimization.class.getSimpleName()).build();new Runner(opt).run();}
}

上面的代码中,我们取消了偏向锁的使用:-XX:-UseBiasedLocking。为啥要取消这个选项呢?因为如果在偏向锁的情况下,如果线程获得锁之后,在之后的执行过程中,如果没有其他的线程访问该锁,那么持有偏向锁的线程则不需要触发同步。

为了更好的理解synchronized的流程,这里我们将偏向锁禁用。

其他的都是我们之前讲过的JMH的常规操作。

接下来就是见证奇迹的时刻了。

分析Assembly日志

我们运行上面的程序,将会得到一系列的输出。因为本文并不是讲解Assembly语言的,所以本文只是大概的理解一下Assembly的使用,并不会详细的进行Assembly语言的介绍,如果有想深入了解Assembly的朋友,可以在文后留言。

分析Assembly的输出结果,我们可以看到结果分为C1-compiled nmethod和C2-compiled nmethod两部分。

先看C1-compiled nmethod:

第一行是monitorenter,表示进入锁的范围,后面还跟着对于的代码行数。

最后一行是monitorexit,表示退出锁的范围。

中间有个add $0x51,%eax操作,对于着我们的代码中的add操作。

可以看到C1—compiled nmethod中是没有进行Loop unrolling的。

我们再看看C2-compiled nmethod:

和C1很类似,不同的是add的值变成了0x144,说明进行了Loop unrolling,同时对应的锁范围也跟着进行了扩展。

最后看下运行结果:


Benchmark              Mode  Cnt     Score     Error  Units
LockOptimization.test  avgt    5  5601.819 ± 620.017  ns/op

得分还不错。

禁止Loop unrolling

接下来我们看下如果将Loop unrolling禁掉,会得到什么样的结果。

要禁止Loop unrolling,只需要设置-XX:LoopUnrollLimit=1即可。

我们再运行一下上面的程序:

可以看到C2-compiled nmethod中的数字变成了原本的0x51,说明并没有进行Loop unrolling。

再看看运行结果:

Benchmark              Mode  Cnt      Score      Error  Units
LockOptimization.test  avgt    5  20846.709 ± 3292.522  ns/op

可以看到运行时间基本是优化过后的4倍左右。说明Loop unrolling还是非常有用的。

总结

本文介绍了循环展开和粗化锁的实际例子,希望大家能够喜欢。

本文的例子https://github.com/ddean2009/learn-java-base-9-to-20

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/jvm-jit-loop-unrolling-lock-coarsening/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

深入理解编译优化之循环展开和粗化锁相关推荐

  1. 小师妹学JVM之:深入理解JIT和编译优化-你看不懂系列

    文章目录 简介 JIT编译器 Tiered Compilation分层编译 OSR(On-Stack Replacement) Deoptimization 常见的编译优化举例 Inlining内联 ...

  2. delete优化_深入理解JIT和编译优化

    点击上方的蓝字关注我吧 程序那些事 简介 小师妹已经学完JVM的简单部分了,接下来要进入的是JVM中比较晦涩难懂的概念,这些概念是那么的枯燥乏味,甚至还有点惹人讨厌,但是要想深入理解JVM,这些概念是 ...

  3. 你真的了解java编译优化吗?15个问题考察自己是否理解

    [摘要] 早期编译过程 晚期编译优化 jvm编译优化学习笔记 早期 第一步: -------词法分析: -------语法分析(注意实际上只是生成一个语法树,还没做语法的校验): -------填充符 ...

  4. 编译优化 | LLVM代码生成技术详解及在数据库中的应用

    简介:作者:长别 1. 前言 随着IT基础设施的发展,现代的数据处理系统需要处理更多的数据.支持更为复杂的算法.数据量的增长和算法的复杂化,为数据分析系统带来了严峻的性能挑战.近年来,我们可以在数据库 ...

  5. 并发高?可能是编译优化引发有序性问题

    摘要:CPU为了对程序进行优化,会对程序的指令进行重排序,此时程序的执行顺序和代码的编写顺序不一定一致,这就可能会引起有序性问题. 本文分享自华为云社区<[高并发]解密导致并发问题的第三个幕后黑 ...

  6. 15个问题自查你真的了解java编译优化吗?

    摘要:为什么C++的编译速度会比java慢很多?二者运行程序的速度差异在哪? 了解了java的早期和晚期过程,就能理解这个问题了. 本文分享自华为云社区<你真的了解java编译优化吗?15个问题 ...

  7. supersu二进制更新安装失败_Q音直播编译优化与二进制集成方案

    一.背景: Q音直播抽离成pod库分别引入到QQ音乐和Fan直播两个独立app中,而对于直播业务来讲,直播SDK通过pod本地引入集成到Demo中进行日常直播业务的开发,通过Demo来精简工程规模,提 ...

  8. 打印异常堆栈_通过异常堆栈丢失谈即时编译优化

    前言 日照充足会让西瓜更甜,那拥有即时编译优化会让Java程序怎么样?本文会初步介绍JVM的即时编译优化特性,并且通过异常堆栈丢失这一常见的现象来进行举例 即时编译优化 Java程序在运行初期是通过解 ...

  9. c++ 输出二进制_Q音直播编译优化与二进制集成方案

    一.背景: Q音直播抽离成pod库分别引入到QQ音乐和Fan直播两个独立app中,而对于直播业务来讲,直播SDK通过pod本地引入集成到Demo中进行日常直播业务的开发,通过Demo来精简工程规模,提 ...

最新文章

  1. Android在桌面上添加开关,多键开关 Andromax v1.1.7
  2. ITK:仅将过滤器应用于图像的指定区域
  3. std::bind 详解及参数解析
  4. Java构造函数的深入理解
  5. 查询数据库中有多少个数据表_您的数据中有多少汁?
  6. mysql: you can't specify target table 问题解决
  7. Java屏蔽输入法_技巧:如何禁止输入法切换到全角状态
  8. sudo报错案例-RHEL6
  9. 【CODEVS1191】数轴染色
  10. linux内核源码分析--内核启动之,Linux内核源码分析之setup_arch (二)
  11. html消息对话框,添加消息对话框 (HTML)
  12. 从X86架构来源开始:谈CPU
  13. 计算机硬件软件的学习
  14. 请问:现正在广告上的丰胸产物是不是实的?
  15. 常用数据库的基因ID
  16. linux 进程调度cfg,选择Linux I / O调度程序
  17. 经常玩电脑正确的坐姿_细说用电脑的正确坐姿
  18. 元素被鼠标掠过控制另一个元素显隐
  19. docker常用命令-docker start
  20. Linux下离线安装Google Chrome

热门文章

  1. socket穿透代理代码(C++版)
  2. 棋牌游戏服务器架构: 总体设计
  3. 反汇编RETN 0x0c的理解
  4. 用Python实现插⼊排序
  5. 深入理解CPU的调度原理
  6. 深入理解Linux socket
  7. 顶级c程序员之路 选学篇-1 深入理解字节,字节序与字节对齐
  8. 最强新一代消息系统,没有之一,不接受反驳!
  9. Spring Boot 2.0 迁移指南
  10. [九]RabbitMQ-客户端源码之Consumer