指令重排
我们知道java在运行的时候有两个地方可能用到重排序,一个是编译器编译的的时候,一个是处理器运行的时候。
那么我们就应该问问为啥要用指令重排序呢?
编译期重排序有啥好处?
CPU计算的时候要访问值,如果常常利用到寄存器中已有的值就不用去内存读取了,比如说
int a = 1;
int b = 1;
a = a + 1;
b = b +1 ;就可能没有
int a = 1;
a = a + 1;
int b = 1;
b = b +1 ;性能好,因为后者的a或b可能在寄存器中了。
处理器为啥要重排序?
一条汇编指令的执行步骤:
取指 IF
译码和取寄存器操作 ID
执行或者有效地址计算 EX
存储器访问 MEM
写回 WB
在CPU工作中汇编指令分多步完成,每一部涉及到的硬件(寄存器)可能不同,于是有了流水线技术来执行指令。
没有流水线技术前,如果同时两个指令过来执行 一个需要5秒,那么两个就需要10秒;有了流水线技术之后,可能就只要6秒。多个指令同时执行时性能显著提升。
流水线技术是一种将指令分解为多步,并让不同指令的各步操作重叠,从而实现几条指令并行处理。
指令1 IF ID EX MEN WB
指令2 IF ID EX MEN WB
指令的每一步都由不同的硬件完成,假设每一步耗时1ms,执行完一条指令需耗时5ms,
每条指令都按顺序执行,那两条指令则需10ms。
但是通过流水线在指令1刚执行完IF,执行IF的硬件立马就开始执行指令2的IF,这样指令2只需要等1ms,两个指令执行完只需要6ms,效率是不是提升巨大!
这个和指令重排有啥关系?
流水线技术并不是说多个汇编指令都能并行执行,还是需要等他其他指令执行完才可以执行(比如ADD指令需要等待LW指令读取寄存器数据完成),那么在这个等待过程中,我们可以让和这个指令不相干的后面的指令先执行(比如另外一个表达式的LW指令),这就是指令重排。
先记住几个指令:
LW(加载数据到寄存器的指令)
ADD(两个定点寄存器的内容相加)
SUB(相减)
SW(把数据从寄存器存储到存储器)
现在来看一下代码 A=B+C 是怎么执行的
这个停顿可以避免吗?
当然是可以的,通过指令重排就可以实现,再看一下下面的例子:
要执行
A=B+C;
D=E-F;
与其让第一个表达式的ADD指令阻塞等待,还不如让第二个表达式的LW指令先执行,注意两个表达式的LW指令操作的是不同的寄存器,所以可以并行执行。
我们写一段代码来试试:
运行结果中:
也就是先用mov把方法里面所需要的三个value加载了,再统一用add进行加法运算。
现在我们把//***哪一行注释掉,运行结果如下:
依然是先把所有value都用mov指令加载后再进行加法运算。 总结起来就是不管代码里这个值使用顺序多靠后,都先用mov加载后再使用add对这个值进行运算。
注意,上面的运行参数为-Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*ReOrder.add1 -XX:+PrintCompilation。 Xcomp 含义是使用编译模式而不是解释模式, -XX:CompileCommand=print,*ReOrder.add1表示只打印这个方法,-XX:+PrintCompilation表示打印方法名称。 需要插件hsdis,编译好后放在jdk的jre的bin的server中就好,具体环境搭建可以参阅这里
参考
最后更新于
这有帮助吗?