Pau*_*lin 23
gcc -O2
Run Code Online (Sandbox Code Playgroud)
编译器可以做得更好.
Ada*_*vis 16
为过滤器,循环缓冲器等选择2的幂.
非常非常方便.
-亚当
Ste*_*eve 12
科学代码中最有用的一个就是pow(x,4)用x*x*x*x.Pow几乎总是比乘法更昂贵.接下来是
for(int i = 0; i < N; i++)
{
z += x/y;
}
Run Code Online (Sandbox Code Playgroud)
至
double denom = 1/y;
for(int i = 0; i < N; i++)
{
z += x*denom;
}
Run Code Online (Sandbox Code Playgroud)
但我最喜欢的低级优化是确定哪些计算可以从循环中删除.它总是更快地进行一次计算而不是N次.根据您的编译器,其中一些可能会自动为您完成.
小智 8
我不一定称之为低级优化,但通过明智地应用缓存,我通过所有低级别技巧的应用程序组合,已经节省了数量级更多的周期.其中许多方法都是特定于应用的.
if或for语句.CPU和编译器不断变化.不管底层代码的技巧,是有道理的3个CPU芯片与前不同的编译器可能实际上是对当前建筑速度较慢,有可能是一个很好的机会,这一招可能会混淆谁是维持在未来这段代码.
++i可以比快i++,因为它避免创建一个临时的.
对于现代C/C++/Java/C#编译器,这是否仍然适用,我不知道.对于具有重载运算符的用户定义类型,它可能是不同的,而在简单整数的情况下,它可能无关紧要.
但我开始喜欢它的语法......它读起来像"增量i",这是一个合理的顺序.
多年以前,有一个不那么聪明的编译器,我从函数内联,行走指针而不是索引数组获得了很大的里程,并且迭代到零而不是最大值.
如果有疑问,对汇编的一点知识将让你看看编译器产生了什么并攻击低效的部分(使用源语言,使用对编译器更友好的结构.)
预先计算值.
例如,如果你的应用不一定需要角度非常精确,而不是sin(a)或cos(a),也许你用圆圈的1/256表示角度,并创建浮点数正弦[]和余弦[]预先计算这些角度的sin和cos.
并且,如果您经常需要一个给定长度的某个角度的矢量,您可能会预先计算所有那些已经乘以该长度的正弦和余弦.
或者,更普遍的说,交易记忆速度.
或者,更一般地说,"所有编程都是一种缓存练习" - Terje Mathisen
有些事情不太明显.例如,遍历二维数组,您可能会执行类似的操作
for (x=0;x<maxx;x++)
for (y=0;y<maxy;y++)
do_something(a[x,y]);
如果您这样做,您可能会发现处理器缓存更喜欢它:
for (y=0;y<maxy;y++)
for (x=0;x<maxx;x++)
do_something(a[x,y]);
或相反亦然.
不要循环展开.不要做Duff的设备.使您的循环尽可能小,其他任何东西都会抑制x86性能和gcc优化器性能.
摆脱分支可能是有用的,所以完全摆脱循环是好的,那些无分支的数学技巧确实有效.除此之外,尽量不要离开L2缓存 - 这意味着如果浪费缓存空间,也应该避免大量的预先计算/缓存.
并且,特别是对于x86,尝试在任何时候保持使用的变量数量.很难说这些编译器会用这种东西做什么,但通常用较少的循环迭代变量/数组索引最终会得到更好的asm输出.
当然,这适用于台式机CPU; 具有快速内存访问速度的慢CPU可以预先计算更多,但在这些日子里,这可能是一个总内存很少的嵌入式系统......
| 归档时间: |
|
| 查看次数: |
5770 次 |
| 最近记录: |