【大话处理器】编写高效代码
一、软件效率
在软件开发中,就叫软件性能剖析。性能剖析工具分析每个函数(有的工具能分析到每个循环)的执行时间。常用的性能剖析软件有:IBM的Rational Quantify、Intel的VTune、AMD的CodeAnalyst,DSP的软件集成开发环境中也自带有这种工具。
二、减少指令数
1. 使用更快的算法:算法,程序设计的灵魂;(高斯的故事)
2. 选用合适的指令:合适的人做合适的事;(主要是处理器的专用汇编指令)
3. 降低数据精度:比特也要省着用;(单精度浮点数绝对值函数fabsf()比双精度fabs()快)
4. 减少函数调用:不要老打断我;
a) 将小函数直接写成语句;
b) 将小函数写成宏;
c) 将函数声明为内联函数;
5. 空间交换时间:将中间结果保存;(Fibonacci数列某项值的计算、Google百度搜索引擎算法)
6. 减少过保护:避免冗余功能;(函数入参检查、保护可适当去掉)
三、减少处理器不擅长的操作
1. 少用乘法;(可转化为移位运算)
2. 少用除法、求余;
3. 在精度允许的条件下,将浮点数定点化;
4. 尽量减少分支;(if和switch跳转语句会打乱流水线执行,影响效率)
5. 将最有可能进入的分支放在if中,而不是else中;
四、优化内存访问
1. 少使用数组,少使用指针;(用局部变量代替)
2. 少使用全局变量;(用局部变量代替)
3. 一次多访问一些数据;
4. 数据对齐访问:对于n字节的变量,它的起始地址应该为n的整数倍;
5. 大数据结构时的Cacheline对齐;(Intel处理器的Cache line大多为64 byte,在对大数据结构分配内存时,起始地址最好为64 byte的整数倍,这样Cachemiss的次数最少。)
6. 程序、数据访问符合Cache的时间、空间局部性;(两重for循环处理二维数组例子;程序组织、存储符合Cache局部性原则)
7. 多线程编程时,避免falsesharing;
8. 自己管理内存的动态分配;(频繁的动态分配和释放内存所带来的危害)
9. 隐藏数据搬移时间;(根据处理器支持,用DMA将SRAM中数据搬移到处理器、Cache预取机制)
五、充分利用编译期进行优化
1. 编译器的结构;(编译原理与编译器构造)
2. 编译器提供了几级优化选项:选择优化级别;(根据具体情况适度优化)
3. 编译器会计算常量;
4. 简化表达式;(消除重复计算)
5. 提取公共代码;(把两个分支中的公共代码提到外面)
6. 循环展开、软件流水;(比如可以使用编译期的预编译指令)
7. 自动向量化;(使用关键字restrict)
8. 高效的数据组织;(编译器对变量、函数分配存储空间,减少Cache miss)
9. 指令并行化;
10. 编译器更懂处理器;
六、 利用多核来加速程序
1. 并行计算;(分工:任务划分、数据划分、数据流划分;Amdahl’s law,阿姆达尔定律:串行与并行分开;多线程编程)
2. OpenMP;(并行编程框架,适合共享内存系统、多核处理器)