1.有序性
1>.JVM会在不影响正确性的前提下调整语句的执行顺序
,思考下面一段代码:
static int i;
static int j;
// 在某个线程内执行如下赋值操作
i = ...;
j = ...;
可以看到,至于是先执行i还是先执行j,对最终的结果不会产生影响.所以,上面代码真正执行时,既可以是:
i = ...;
j = ...;
也可以是:
j = ...;
i = ...;
这种特性称之为"指令重排",单线程环境下没有问题,但是在多线程环境下"指令重排"会影响正确性.为什么要有重排指令这项优化呢?从CPU执行指令的原理来理解一下吧;
指令重排序的目的是为了实现指令级的并行效果!
2.指令级并行原理
2.1.相关名词
1>.Clock Cycle Time
主频的概念大家接触的比较多,而CPU的Clock Cycle Time(时钟周期时间),等于主频的倒数,意思是CPU能够识别的最小时间单位,比如说4G主频的CPU的Clock Cycle Time就是0.25 ns,作为对比,我们墙上挂钟的Cycle Time是1s;
例如,运行一条加法指令一般需要一个时钟周期时间;
2>.CPI
有的指令需要更多的时钟周期时间,所以引出了CPI(Cycles Per Instruction)指令平均时钟周期数(/多少个时钟周期);
3>.IPC
IPC(Instruction Per Clock Cycle)即CPI的倒数,表示每个时钟周期能够运行的指令数;
4>.CPU执行时间
程序的CPU执行时间,即我们前面提到的user + system时间.可以用下面的公式来表示:
程序CPU执行时间 = 指令数 * CPI * Clock Cycle Time
2.2.鱼罐头的故事
加工一条鱼需要50分钟,只能一条鱼、一条鱼顺序加工…
可以将每个鱼罐头的加工流程细分为5个步骤:
去鳞清洗 10分钟
蒸煮沥水 10分钟
加注汤料 10分钟
杀菌出锅 10分钟
真空封罐 10分钟
优化:
即使只有一个工人,最理想的情况是: 他能够在10分钟内同时做好这5件事,因为对第一条鱼的真空装罐,不会影响对第二条鱼的杀菌出锅…
2.3.指令重排序优化
事实上,现代处理器会设计为一个时钟周期完成一条执行时间最长的CPU指令.为什么这么做呢?可以想到指令还可以再划分成一个个更小的阶段,例如,每条指令都可以分为:{取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回}这5个阶段;
在不改变程序结果的前提下,这些指令的各个阶段可以通过重排序和组合来实现指令级并行,这一技术在80年代中叶到90年代中叶占据了计算架构的重要地位;
提示:分阶段,分工是提升效率的关键!
***注意: 指令重排的前提是,重排指令不能影响结果,例如:
// 可以重排的例子
int a = 10; // 指令1
int b = 20; // 指令2
System.out.println( a + b );
// 不能重排的例子
int a = 10; // 指令1
int b = a - 5; // 指令2
2.4.支持流水线的处理器
1>.现代CPU支持多级指令流水线,例如支持同时执行{取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回}的处理器,就可以称之为五级指令流水线.
这时CPU可以在一个时钟周期内,同时运行五条指令的不同阶段(相当于一条执行时间最长的复杂指令),IPC = 1,本质上,流水线技术并不能缩短单条指令的执行时间,但它变相地提高了指令地吞吐率;
题外话:
奔腾四(Pentium 4)支持高达35级流水线,但由于功耗太高被废弃!
2.5.SuperScalar处理器
1>.大多数处理器包含多个执行单元,并不是所有计算功能都集中在一起,可以再细分为整数运算单元、浮点数运算单元等,这样多条指令也可以做到并行获取、译码等,CPU可以在一个时钟周期内执行多于一条指令,IPC > 1;