【发布时间】:2009-03-19 15:50:00
【问题描述】:
我正在编写一些优化的 C 代码,这些代码基本上通过一个数组运行并对每个元素执行一些操作。它的作用取决于元素的当前值,例如:
for (i=0; i < a_len; i++) {
if (a[i] == 0) {
a[i] = f1(a[i]);
} else if (a[i] % 2 == 0) {
a[i] = f2(a[i]);
} else {
a[i] = 0;
}
在动态语言工作多年后,我回到了 C,我的实践一直是尝试编写简单的代码,而不是为我可以直接引用的东西创建大量局部变量,例如 a[i]多于。 我非常清楚最好的做法是编写可读的代码并相信编译器比你更聪明并且会进行良好的优化。
如果我在汇编程序中编写上面的代码,我会将 a[i] 加载到寄存器中一次,然后每次只使用该值,因为我知道 a[] 是私有内存并且不会在引用之间更改。但是,即使是智能编译器也可能每次都加载,因为它不能确定内存没有改变。 (或者我是否必须显式声明“a” volatile 以使编译器不进行此优化?)。
所以,我的问题是:我是否应该通过这样的局部变量重写来获得更好的性能:
for (i=0; i < a_len; i++) {
val = a[i];
if (val == 0) {
a[i] = f1(val);
} else if (val % 2 == 0) {
a[i] = f2(val);
} else {
a[i] = 0;
}
或者像 -O3 这样的东西会自动为我处理这个问题吗?我正在优化的代码需要几天时间才能运行,因此即使是适度的改进也会产生影响。
【问题讨论】:
-
很大程度上取决于“a”是什么(从哪里来。如果它是一个函数参数,事情会比它是一个本地数组更复杂)。在你的情况下,所有子句都是互斥的,我认为一个体面的编译器会产生相同的汇编代码。
-
使用寄存器 T val = a[i];然后将其存储在寄存器中以供以后访问。基本上你期望编译器为你做/优化什么,但你确定。
-
公用子表达式消除应该让编译器为您执行此操作。此外,对于您的特定示例,假设您的编译器没有特殊情况,使用 (!(val & 0x1)) 测试均匀性比 val %2 == 0 更快。
-
当然,如果 a[] 是 volatile,那么 CSE 就不可能发生,这两个例子可能不等价。
标签: c arrays optimization