使用Intel编译器(5)PGO(2)PGO性能的提升和一些tips

来源:互联网 发布:js object[] 编辑:程序博客网 时间:2024/05/19 08:39
参考手册:

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011Update/compiler_c/index.htm


说明:本系列文章为个人笔记,如有不正确之处,请参考官方相关文档,如果错误发现,我会尽量更新修改。另外,以下内容不保证对于所有版本的编译器都正确,编译器的实现也可能有一些变化之处,具体参考官方文档。


更多说明请参考http://blog.csdn.net/gengshenghong/article/details/7034748中补充说明部分。


(1) PGO与分支预测的简单理解

PGO在性能提升方面,一个能工作很好的情况是代码有频繁执行的分支,而且分支在编译时难以预测。一个最常见的例子,就是错误处理的代码,一般而言,错误处理是为了防止代码出错,所以大部分的错误处理的分支,可能在大部分时候都是false。
为了理解这个问题,我以switch分支举例的代码片段如下:

void processError(int retcode){switch(retcode){case 0:processError0();case 1:processError1();case 2:processError2();case 3:processError3();case 4:working();// if no error.default:processErrorD();}}....processError(ret);...processError(ret);...processError(ret);

假设,processError是一个错误处理函数,那么,一般来说,错误不会经常发生,可能对于上面的代码,大部分情况下,那个processError的参数会是4,那么一个问题是,对于上面的代码,大部分情况下,都需要对switch判断是否为0、1、2、3、4才会知道该执行working()的代码,一个改进的情况是:把case 4的情况写在最前面,那么大部分情况下,可能只需要一次判断就能得到结果。当然,这里我是故意这么写的,只是为了方便理解所谓的"分支预测"对性能的影响。PGO能更好的处理这样的switch的问题,更深入的情况也更实际的情况是,编译器可能会尽量减少"冷代码"插入到"热代码"来提高指令cache的性能,所谓的冷代码,就是不频繁执行的代码,比如上面的processError0等,反之,频繁执行的就是热代码了,比如上面的working,如何提高指令cache的性能?这是很容易理解的,将频繁执行的热代码尽量靠近,那么就可能会较少不断的刷新指令cache来读取指令等。另外,还有一个可能的优化,比如可能进行一些内联等。总之,对于这些需要动态运行才能知道的信息,PGO能更好的优化,对于静态的信息,IPO、HLO等就可以进行很好的优化了。

说明:这里用一个简单的例子分析了一下对分支的情况的优化,上面的只是个人的理解,实际编译器能进行的优化可能更多。另外,PGO优化也不仅仅是针对分支预测有优化作用。

1. 在执行辅助可执行代码和根据反馈来编译之前(即2、3步骤之间), 减少对程序的修改。编译器在反馈编译(第三步)的时候,对于执行辅助可执行文件后修改过的函数,会忽略其修改前生成的动态信息。这是很容易理解的,而且也不是问题,一般没人会在这期间去修改代码,如果修改了,一般都会重新用第一步编译一下并运行一下新的辅助可执行文件。

2. 了解程序调用最频繁的部分。对于某些程序,会有输入的数据,不同的输入,执行的分支可能会不一样,对于这样的情况,尽量使用大部分情况下可能的输入来运行辅助可执行文件,以免影响编译器的判断。

对于PGO和其它一些优化一起使用,比如IPO等的时候,在PGO的第一步(prof-gen)的时候,一般只需要使用默认的选项即可(O2)。只需要在第三步(prof-use)的时候使用这些选项,如果在prof-gen的时候使用了IPO等选项,编译器会忽略这些优化。