窥孔优化 peephole optimization

http://www.lingcc.com/2010/10/13/11308/#more-11308

窥孔优化

虽然很多产品级的编译器在经过仔细的指令选择和寄存器分配后能产生不错的汇编代码。但还有一种基于汇编代码的优化方式,这种方式利用目标CPU能提供的指令和特性,能很好的提升性能。窥孔优化就是此类优化中最典型的。
窥孔优化,顾名思义,是一种很局部的优化方式,编译器仅仅在一个基本块或者多个基本块中,针对已经生成的代码,结合CPU自己指令的特点,通过一些认为可能带来性能提升的转换规则,或者通过整体的分析,通过指令转换,提升代码性能。别看这些代码转换很局部,很小,但可能会带来很大的性能提升。

这个窥孔,你可以认为是一个滑动窗口,编译器在实施窥孔优化时,就仅仅分析这个窗口内的指令。每次转换之后,可能还会暴露相邻窗口之间的某些优化机会,所以可以多次调用窥孔优化,尽可能提升性能。

窥孔优化可以在四个方面寻找优化机会:冗余指令删除,包括冗余的load和store指令以及死代码(不会执行的代码);控制流优化;强度削弱;利用特有指令。

删除冗余load和store:比如这段汇编代码:
sh $0,6($sp)
sh $0, 4($sp)
sh $0, 2($sp)
sh $0, 0($sp)
ldc1 $f1, 0($sp)
完全可以使用指令xor $f1,$f1,$f1这样可以省掉5次访存操作,性能的提升非常明显。上面sh是store 16bit到某个地址,ldc1是load 64bit到某个寄存器。xor是异或指令。
但是这种指令序列的转换和合成有个前提,必须保证这些指令按照顺序执行,即这些指令之间,不能有其他标号,即入口。也就是说这些指令必须在一个基本块中。当然,你也可以在编译器较前面阶段的优化中,针对该操作,做变换。这样到了窥孔优化时,就不再会有这样的代码了。

删除死代码:有些代码可能用于不会被执行到,这样在窥孔优化阶段,如果发现这样的代码,就可以直接删除。典型的方式是优化双跳转,即第一条跳转指令的目的地址还是一条跳转指令时,可以删除后一条跳转指令,并修改第一条跳转指令的目标地址。另外,对于不可能进入的分支也可以使用这种方式删除。

控制流优化:中间代码生成阶段,很可能经常产生一些跳转到跳转指令,跳转到分支跳转、分支跳转到跳转之类的指令,都可以在窥孔优化中想办法解决掉,当然你也可以在中间代码中优化它们。一些分支被删除后,可能还存在一些不会被到达的标号(label),也可以顺便删除之,这样就会提升基本块的大小,增加优化机会。

强度削弱:即利用代价较小的指令或操作替代代价较大的指令或操作,从而提升性能。比如x=x+0, x=x*1之类的操作就能直接避免,x=x*2,x=x/2之类的操作可以使用左移或右移实现。x^2之类的指数运算可以削弱为x*x的乘法运算,浮点数除以常数的运算可以转换为浮点数乘以常数的倒数。这些都是优化方式。

充分利用特有指令:CPU都会提供一些特殊指令完成特殊操作,比如DSP芯片中可能有复杂的数字信号处理指令,龙芯中有乘加指令以及一些向量扩展指令。还有一些CPU可能提供自增、自减、取绝对值指令。这些都能在窥孔优化中生成。提升程序的运行性能。

参考:

  1. Compilers, Principles, Techniques, and Tools, 2ed, Alfred V. Aho etc.  P549-554

你可能感兴趣的:(javascript,webkit)