从三种编译优化方式学习改善自己的代码
注:以下内容为笔者学习《C++反编译与逆向分析技术揭秘》一书笔记,纯粹为了加深记忆。更多内容请查阅原书。
一、流水线优化
多流水线结构是为了提高效率而设计,当一条流水线在处理一条代码的时候,另一条流水线开始对下一条指令进行取指令、译码等工作,当上一条指令处理完毕,开始执行下一条。
由流水线机制要避免的有两点:
- 寄存器争用
- 内存地址争用
即避免相邻的两条指令对同一寄存器或内存地址进行读写操作。如下面的代码:
call _printf add eax , 12345678h imul eax, eax, 0Ah add esp, 8
编译器会优化成:
call _printf add eax , 12345678h ;add改变eax的值 add esp, 8 ;流水线优化将用到同一寄存器的两条指令用这条代码隔开 imul eax,eax,0Ah ;imul指令将eax*10后放入eax
二、分支优化
在流水线工作模式下,遇到分支结构,利用Branch Target Buffer预测并读取指令的目标地址,当遇到转移指令,若BTB里有记录,则把它当做目标地址,若等于实际转移的地址,则并行成功,否则流水线被冲洗,同一分之多次预测失败则更新BTB中的目标地址。因此,为减少预测失败的次数,我们在编写多重循环的时候应该将大循环写在内部。
//第一种写法:过程重复100次,预测失败101次 //第一次不预测,预测成功99次,最后一次预测失败退出。 for(j=0;j<100;j++){ //第一次不预测,以后每次循环预测成功9次,最后一次预测失败 for (i=0;i<10;i++){ do something... } } //第二种写法:过程重复10次,预测失败11次 //第一次不预测,循环结束时预测失败 for(i=0;i<10;i++){ //第一次不预测,以后每次循环预测成功99次,结束时预测失败一次 for (j=0;j<100;j++){ do something... } }
三、高速缓存优化
为了提高内存访问效率,出现了cache来寻访经常访问的数据和代码,处理器访问内存数据时候,先查看cache中是否有虚拟地址记录,若有则直接hit,无需访问内存单元,若没找到,则转换VA查找。
cache优化总结:
1.数据对齐:cache不保存VA二进制低位,因此保存数据的单位为2n字节,编译器设置变量地址时候会按照4字节边界对齐。
2.数据集中:由于cache在抓取数据的同时会抓取其周围的数据,因此尽量将访问率高的数据放在一起。
3.减少体积:访问次数多额代码尽量减少体积放入cache中。
虽然现在的编译器在编译程序的时候会为我们的代码做很多优化工作,但良好的编程习惯不能依赖编译器的优化,还是要从自身养成~~