x86中历史上推荐使用哪种C优化实践不再有效?

Mys*_*1tx 27 c optimization

由于x86的C编译器(即GCC和锵)的进步,这被认为提高效率许多编码做法已不再使用,因为编译器能够做得更好优化比人类的代码(例如位移位与乘法).

这些具体做法是什么?

dus*_*uff 18

在通常推荐的优化中,给定现代编译器的几个基本上没有成果包括:

数学变换

现代编译器理解数学,并在适当的时候对数学表达式进行转换.

即使在低优化级别,现代编译器也已经执行了诸如乘法到加法的转换或者常数乘法或除法到位移的优化.这些优化的示例包括:

x * 2  ->  x + x
x * 2  ->  x << 1
Run Code Online (Sandbox Code Playgroud)

请注意,某些特定情况可能有所不同 例如,x >> 1不一样x / 2; 用一个替换另一个是不合适的!

此外,其中许多建议的优化实际上并不比它们替换的代码快.

愚蠢的代码技巧

我甚至不确定该怎么称呼它,但像XOR交换(a ^= b; b ^= a; a ^= b;)的技巧根本不是优化.它们只是派对技巧 - 它们比明显的方法更慢,更脆弱.不要使用它们.

register关键字

许多现代编译器忽略了这个关键字,因为它的预期含义(强制变量存储在寄存器中)对于给定当前寄存器分配算法是没有意义的.

代码转换

编译器将在适当的情况下自动执行各种代码转换.经常推荐用于手动应用的几种这样的转换,但在应用时很少有用,包括:

  • 循环展开.(当不加区分地应用时,这实际上通常是有害的,因为它会增加代码大小.)
  • 功能内联.(将函数标记为static,并且在启用优化时通常会在适当的位置内联.)

  • 循环展开和强制内联仍然可以在正确完成时提供巨大的加速.你提到的其他一切现在几乎都是普遍真实的. (3认同)

cma*_*ter 8

一种这样的做法是通过使用数组指针的数组而不是真正的2D数组来避免乘法.


旧做法:

int width = 1234, height = 5678;
int* buffer = malloc(width*height*sizeof(*buffer));
int** image = malloc(height*sizeof(*image));
for(int i = height; i--; ) image[i] = &buffer[i*width];

//Now do some heavy computations with image[y][x].
Run Code Online (Sandbox Code Playgroud)

这曾经更快,因为乘法过去非常昂贵(大约30个CPU周期),而内存访问几乎是免费的(只是在20世纪90年代才增加了缓存,因为内存跟不上完整的CPU速度).


但是乘法变得很快,一些CPU能够在一个CPU周期内完成它们,而内存访问根本没有跟上速度.所以,现在这段代码可能更具性能:

int width = 1234, height = 5678;
int (*image)[width] = malloc(height*sizeof(*image));

//Now do some heavy computations with image[y][x],
//which will invoke pointer arithmetic to calculate the offset as (y*width + x)*sizeof(int).
Run Code Online (Sandbox Code Playgroud)

目前,仍然有一些CPU,其中第二个代码不是更快,但大的乘法惩罚不再与我们.