窥孔 优化,顾名思义,是一种很局部的优化方式,编译器仅仅在一个 基本块或者多个基本块中,针对已经生成的代码,结合CPU自己指令的特点,通过一些认为可能带来性能提升的转换规则,或者通过整体的分析,通过指令转换,提升代码性能。别看这些代码转换很局部,很小,但可能会带来很大的性能提升。
这个窥孔,你可以认为是一个滑动窗口, 编译器在实施窥孔优化时,就仅仅分析这个窗口内的指令。每次转换之后,可能还会暴露相邻窗口之间的某些优化机会,所以可以多次调用窥孔优化,尽可能提升性能。
(1)
优化对象既可以是中间代码级,也可以是目标代码级。(2)每次处理的只是一组相邻指令,相当于将一 组相邻指令暴露在一个优化窗口中(正如“窥孔”的含义)。 (3)对优化对象进行线性扫描。(4)优化后产生的结果可能 会给后面的代码提供进一步优化的机会。(5)窥孔优化程序通常很小,只需很少的内存,执行速度很快。
窥孔优化可以在四个方面寻找优化机会:冗余指令删除,包括冗余的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可能提供自增、自减、取绝对值指令。这些都能在窥孔优化中生成。提升程序的运行性能。