编译器重排序

在一些讲Java并发编程的书中,经常会出现JMM内存模型、volatile关键字、重排序、乱序执行等字眼,导致了有些刚开始学习Java并发编程的小伙伴一脸懵逼:这都是啥啊?

今天我们就来说一下这些书中提到的一个概念,编译器重排序。

这里先说明一点,下提到的编译器指的是将高级语言编译成汇编语言(机器码)的编译器(比如说GCC、clang、甚至JIT),而不是Javac这种编译成中间语言(JVM字节码)的编译器,当然不同的Java编译器生成的JVM字节码的方式可能也各有不同,但这并不在本文的讨论范围之内。

注:本文实验环境基于Windows10中wsl2下的debian版本
在这里插入图片描述

1. 什么是编译器重排序

1.1 定义

编译器重排序:编译器会对高级语言(本文里特指C/C++)的代码进行分析,当编译器认为你的代码可以优化的话,编译器会选择对代码进行优化然后生成汇编代码,当然编译器的优化满足特定的条件,这里要说一下大名鼎鼎的as-if规则:

Allows any and all code transformations that do not change the observable behavior of the program.

也就是说在不影响这段代码结果的前提下,编译器可以使用任意一种方式对代码进行编译,这也就给了编译器充分的空间对代码进行优化,从而提高代码的运行效率。

1.2 举个例子

下面是一段简单的C语言代码:

int a, b;
void foo(void)
{
   
   
	a = b + 11;
	b = 0;
}

这段代码逻辑很简单,但是对于编译器来说,问题就没有这么简单了。我们来看一下使用aarch64-linux-gnu-gcc使用-O2参数,让编译器在O2级别优化的情况下编译上述代码(得到RAM汇编),使用objdump工具查看foo()方法反汇编结果:

0000000000000750 <foo>:
 750:   90000080        adrp    x0, 10000 <__FRAME_END__+0xf6b8>
 754:   90000081        adrp    x1, 10000 <__FRAME_END__+0xf6b8>
 758:   f947dc00        ldr     x0, [x0, #4024] // 取b内存地址
 75c:   f947e821        ldr     x1, [x1, #4048] // 取a内存地址
 760:   b9400002        ldr     w2, [x0]        // 寄存器w2 = b(内存地址)
 764:   b900001f        str     wzr, [x0]       // b(内存地址) = 0
 768:   11002c40        add     w0, w2, #0xb    // 寄存器w0 = b + 11 
 76c:   b9000020        str     w0, [x1]        // w0寄存器的值存入a(内存)
 770:   d65f03c0        ret
 774:   d503201f        nop

我们发现,编译得到的汇编代码和我们原本的C语言代码不顺序并不一致,而是相当于如下C语言代码:

	int a, b
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值