考虑以下代码片段,显示一些简单的算术运算
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));
答案 0 :(得分:7)
GCC(以及大多数C ++编译器)都没有重构代数表达式。
这主要是因为就GCC和通用软件算法而言,行
double x = 0.5 * (4.6 + 6.7);
double y = 0.5 * 4.6 + 0.5 * 6.7;
assert(x == y); //Will probably fail!
不保证评估完全相同的数字。如果没有这种保证,海湾合作委员会就无法优化这些结构。
此外,操作顺序可能很重要。例如:
int x = y;
int z = (y / 16) * 16;
assert(x == z); //Will only be true if y is a whole multiple of 16
代数地,这两行应该是等价的,对吧?但如果y
是int
,它实际上会做的是使x等于“y
四舍五入到16的整数倍”。有时,这是预期的行为(就像你是字节对齐一样)。其他时候,这是一个错误。重要的是,两者都是有效的计算机代码,并且两者都可以根据具体情况而有用,如果GCC围绕这些结构进行优化,则会阻止程序员代理他们的代码。
答案 1 :(得分:1)
是的,包括gcc在内的优化器会对此类型进行优化。但不一定是你引用的表达方式。您必须考虑可能的溢出。例如,(a + a) - a
可能会优化为a
。另一个可能的优化示例是a*a*a*a
到temp = a*a; (temp)*(temp)
通过读取输出汇编代码,可以观察给定编译器是否优化引用的表达式。
不,默认情况下,这种类型的优化不会与浮点一起使用(除非优化器可以证明没有精度丢失)。请参阅Are floating point operations in C associative?您可以让gcc使用-fassociative-math
选项执行此操作。 自担风险。