去年在安宁庄的时候, 有个同事阐述了一个观点:php中的if else 在执行时考虑到效率的原因,不会按我们的代码的顺序一条一条去试,而是随机找出一个分支,执行,如果不对,再随机找到一个分支
当时由于种种原因,也没过多去想这个问题,最近查了下资料,发现里面的学问还挺大的
php解释器是由c编写的,是个经编译生成的二进制文件, 我们编写的PHP代码相当于这个C程序的参数,只不过这个参数是个一个的文件, 这个C程序要解析这个php文件,产生相应的opcode,再去执行opcode对应的函数,每一部操作都是由C函数来实现
查询opcode含义的利器: http://www.laruence.com/2008/11/20/640.html#ZEND_JMP_.28Opcode_42.29
<?php if($a == 1){ echo "a is 1"; }else if($a == 2){ echo "a is 2"; }else{ echo "a is x"; }
对于上面的php代码来说,最终执行的opcode是
------------------------------------------------------------------------------------- 2 0 E > IS_EQUAL ~0 !0, 1 1 > JMPZ ~0, ->4 3 2 > ECHO ‘a+is+1‘ 4 3 > JMP ->9 4 > IS_EQUAL ~1 !0, 2 5 > JMPZ ~1, ->8 5 6 > ECHO ‘a+is+2‘ 6 7 > JMP ->9 7 8 > ECHO ‘a+is+x‘ 9 9 > > RETURN 1
可以看到在执行php时, 是一条一条去执行的
1.先判断 $a 是否 等于 1
2.如果不等于1,为false, 就JMPZ 到第4条命令,去比较 $a 是否 等于2
如果等于1, echo "a is 1"; 然后 无条件跳转 JMP 第9行 return 了
3.如果 $a 不等于2 ,即为false, 就JMPZ 到第8条命令, echo "a is x"
如果 $a 等于2,直接echo "a is 2", 然后执行 JMP 第9行 return 了
所以,php编写的程序,对C函数来说,还是要一步的一步去执行的
如果这个文件被执行100次,有90次 $a=3, 那么解释器每次都要判断 $a 是否等于1和 $a 是否等于2, 尽管第三个分支是满足条件的,如果是C编写的程序, CPU会针对某种策略挑选一个分支来执行, 对应上面的分支来说,CPU会直接取出第三个分支的指令,然后执行。
从486开始,CPU开始具备流水线这个特性,指令流水线由5,6个不同功能的工作单元组成,将一个x86指令也拆分成5,6个步骤,分别送往不同的工作单元,来达到同时执行多个指令的目的,现在的CPU支持30级的流水线,也就意味着流水线上有30个工作单元,对应的X86指令也拆分成30个步骤。
注:CPU执行的是二进制数据,代码经过汇编编译后,生成一条条二进制指令
例如 int a=1; 对应的汇编是mov $1, %eax; 对应的机器码可能是00011100011
在执行文件时,根据局部性原理,想关的指令都要加载到CPU缓存中,
一般一条指令的完成 分四个步骤:
1.取指令
2.翻译指令 (看是赋值,还是计算,从内存什么地方取数据)
3.执行指令
4.写指令结果 (要么写回内存,要么写到寄存器)
取指令 翻译指令 执行指令 写指令结果
命令1 命令1
命令2
取指令单元取出指令1后,翻译指令单元开始 翻译指令1时,取指令单元可 取出指令2了
如果CPU不这么做,等到指令1完成上面四个步骤后,指令2才开始进行,那效率太低了
流水化中的单元分的更详细, 更多的指令可以并行处理,但速度不见得快,因为有分支的出现,如果没有命中第一个分支,后面的指令将作废, 需要清空后面所有的指令, 然后中载命中地址的指令,再运行
在有5个分支的情况下,若采取随机挑选一个分支 执行的话,每次赌该分支命中的概率只有五分之一, 于是CPU分支预测功能就出现了。
分支预测分静态和动态
静态分支预测:由编译器决定哪个分支可能被CPU命中,一般是第一个分支,即 if 后面的逻辑,而不是后面else的逻辑
动态分支预测:在CPU硬件中开辟一块缓存,专门记录每个分支最近几次的命中情况,然后做出预测,显然这种方法能及时调整策略,有更好的远詹性,但CPU压力会大些,不过还好。
分支地址只有在流水线指令执行阶段才能计算出来,为了避免等待,需要在译码阶段进行预测
Two-Level分支预测方法使用了两种数据结构,一种是BHR(Branch History Register);而另一种是PHT(Pattern History Table)。其中BHR由k位组成(可理解为记录K次某个分支的执行结果),用来记录每一条转移指令的历史状态,而PHT表含有2k个Entry组成,而每一个Entry由两位Saturating Counter组成。BHR和PHT的关系如图3?10所示。
假设分支预测单元在使用Two-Level分支预测方法时,设置了一个PBHT表(Per-address Branch History Table)存放不同指令所对应的BHR。在PBHT表中所有BHR的初始值为全1,而在PHT表中所有Entry的初始值值为0b11。BHR在PBHT表中的使用方法与替换机制与Cache类似。
当分支预测单元分析预测转移指令B的执行时,将首先从PBHT中获得与转移指令B对应的BHR,此时BHR为全1,因此CPU将从PHT的第11…11个Entry中获得预测结果0b11,即Strongly Taken。转移指令B执行完毕后,将实际执行结果Rc更新到BHR寄存器中,并同时更新PHT中对应的Entry。
当CPU再次预测转移指令B的执行时,仍将根据BHR索引PHT表,并从对应Entry中获得预测结果。而当指令B再次执行完毕后,将继续更新BHR和PHT表中对应的Entry。当转移指令的执行结果具有某种规律(Pattern)时,使用这种方法可以有效提高预测精度。如果转移指令B的实际执行结果为001001001….001,而且k等于4时,CPU将以0010-0100-1001这样的循环访问BHR,因此CPU将分别从PHT表中的第0010、0100和1001个Entry中获得准确的预测结果。
由以上描述可以发现,Two-Level分支预测法具有学习功能,并可以根据转移指令的历史记录产生的模式,在PHT表中查找预测结果。该算法由T.Y. Yeh and Y.N. Patt在1991年提出,并在高性能处理器中得到了大规模应用。
Two-Level分支预测法具有许多变种。目前x86处理器主要使用“Local Branch Prediction”和“Global Branch Prediction”两种算法。
在“Local Branch Prediction”算法中,每一个BHR使用不同的PHT表,Pentium II和Pentium III处理器使用这种算法。该算法的主要问题是当PBHT表的Entry数目增加时,PHT表将以指数速度增长,而且不能利用其它转移指令的历史信息进行分支预测。而在“Global Branch Prediction”算法中,所有BHR共享PHT表,Pentium M、Pentium Core和Core 2处理器使用这种算法。
在高性能处理器中,分支预测单元对一些特殊的分支指令如“Loop”和“Indirect跳转指令”设置了“Loop Prediction”和“Indirect Prediction”部件优化这两种分支指令的预测。此外分支预测单元,还设置了RSB(Return Stack Buffer),当CPU调用一个函数时,RSB将记录该函数的返回地址,当函数返回时,将从RSB中获得返回地址,而不必从堆栈中获得返回地址,从而提高了函数返回的效率。
目前在高性能处理器中,动态分支预测的主要实现机制是CPU通过学习以往历史信息,并进行预测,因而Neural branch predictors机制被引入,并取得了较为理想的效果,本节对这种分支预测技术不做进一步说明。目前指令的动态分支预测技术较为成熟,在高性能计算机中,分支预测的成功概率在95%~98%之间,而且很难进一步提高。
参考:http://blog.sina.com.cn/s/blog_6472c4cc0100qxd2.html
http://tonysuo.blogspot.hk/2013/12/computer-architecture-5.html
http://blog.hesey.net/2013/03/branch-prediction-in-pipeline.html
http://wenku.baidu.com/view/48833667ddccda38376bafa2.html
http://blog.sina.com.cn/s/blog_5a82024e0100e5lm.html
//大话处理器
http://blog.csdn.net/muxiqingyang/article/details/6677425
http://cyukang.com/2012/07/11/branch_prediction.html
http://blog.csdn.net/wahaha_nescafe/article/details/8500094
https://www.zhihu.com/question/23973128
http://blog.sina.com.cn/s/blog_6556314c0100hamf.html
http://blog.sina.com.cn/s/blog_6556314c0100hamt.html
http://blog.sina.com.cn/s/blog_6556314c0100hamj.html
http://blog.sina.com.cn/s/blog_6556314c0100hamh.html
https://www.zhihu.com/question/23973128