考虑下面的代码片段,显示一些简单的算术运算
int result = 0;
result = c * (a + b) + d * (a + b) + e;
要在上面的表达式中获得结果,cpu将需要执行两次整数乘法和三次整数加法 . 但是在代数上,上面的表达式可以简化为下面的代码 .
result = (c + d) * (a + b) + e
这两个表达式在代数上是相同的,但第二个表达式只包含一个乘法和三个加法 . gcc(或其他编译器)是否能够自己进行这种简单的优化 .
现在假设编译器足够智能以进行这种简单的优化,它是否能够优化更复杂的东西,例如Trapezoidal rule(用于数值积分) . 下面的示例近似于 sin(x)
下的区域,其中 0 <= x <= pi
的步长为pi / 4(为简单起见,小) . 请假设所有文字都是运行时变量 .
#include <math.h>
// Please assume all literals are runtime variables. Have done it this way to
// simplify the code.
double integral = 0.5 * ((sin(0) + sin(M_PI/4) * (M_PI/4 - 0) + (sin(M_PI/4) +
sin(M_PI/2)) * (M_PI/2 - M_PI/4) + (sin(M_PI/2) + sin(3 * M_PI/4)) *
(3 * M_PI/4 - M_PI/2) + (sin(3 * M_PI/4) + sin(M_PI)) * (M_PI - 3 * M_PI/4));
现在,使用梯形规则简化后,可以像这样编写上述函数 . 这大大减少了获得相同答案所需的乘法/除法的数量 .
integral = 0.5 * (1 / no_steps /* 4 in th case above*/) *
(M_PI - 0 /* Upper and lower limit*/) * (sin(0) + 2 * (sin(M_PI/4) +
sin(3 * M_PI/4)) + sin(M_PI));
2 回答
GCC(以及大多数C编译器,就此而言)并不重构代数表达式 .
这主要是因为就GCC和通用软件算术而言,线路
不保证评估完全相同的数字 . 如果没有这种保证,GCC无法优化这些结构 .
此外,操作顺序可能很重要 . 例如:
在代数上,这两行应该是等价的,对吧?但是如果
y
是int
,它实际上会做的是使x等于“y
四舍五入到16的较低整数倍” . 有时,'s intended behavior (Like if you'字节对齐) . 其他时候,这是一个错误 . 重要的是,两者都是有效的计算机代码,并且两者都可以根据具体情况使用,如果GCC围绕这些结构进行了优化,它将阻止程序员对其代码进行代理 .是的,优化器,包括gcc,做这种类型的优化 . 但不一定是你引用的表达方式 . 您必须考虑可能的溢出 . 例如,
(a + a) - a
可能会优化为a
. 另一个可能的优化示例是a*a*a*a
到temp = a*a; (temp)*(temp)
通过读取输出汇编代码,可以观察给定编译器是否优化引用的表达式 .
不,默认情况下,这种类型的优化不会与浮点一起使用(除非优化器可以证明没有精度丢失) . 请参阅Are floating point operations in C associative?您可以让gcc使用
-fassociative-math
选项执行此操作 . 在你自己的危险 .