gcc 是否以代数方式优化 c++ 代码,如果是,优化到什么程度?
Does gcc optimize c++ code algebraically and if so to what extent?
考虑以下显示一些简单算术运算的代码
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));
现在上面的函数用梯形法则简化后就可以写成这样了。这大大减少了获得相同答案所需的 multiplications/divisions 的数量。
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));
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!
不保证得到完全相同的数字。没有这种保证,GCC 无法优化这些结构。
此外,操作顺序可能很重要。例如:
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 围绕这些结构进行优化,它将阻止程序员对他们的代码进行代理。
是的,包括 gcc 在内的优化器会进行此类优化。不一定是您准确引用的表达式,或其他任意复杂的表达式。但是更简单的表达式,例如 (a + a) - a
可能会被优化为 a
。另一个可能的优化示例是 a*a*a*a
到 temp = a*a; (temp)*(temp)
给定的编译器是否优化了您引用的表达式,可以通过阅读输出的汇编代码来观察。
不,默认情况下这种类型的优化不与浮点一起使用(除非优化器可以证明没有准确性丢失)。请参阅 Are floating point operations in C associative? 您 可以 让例如 gcc 使用 -fassociative-math
选项来执行此操作。 后果自负。
考虑以下显示一些简单算术运算的代码
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));
现在上面的函数用梯形法则简化后就可以写成这样了。这大大减少了获得相同答案所需的 multiplications/divisions 的数量。
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));
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!
不保证得到完全相同的数字。没有这种保证,GCC 无法优化这些结构。
此外,操作顺序可能很重要。例如:
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 围绕这些结构进行优化,它将阻止程序员对他们的代码进行代理。
是的,包括 gcc 在内的优化器会进行此类优化。不一定是您准确引用的表达式,或其他任意复杂的表达式。但是更简单的表达式,例如 (a + a) - a
可能会被优化为 a
。另一个可能的优化示例是 a*a*a*a
到 temp = a*a; (temp)*(temp)
给定的编译器是否优化了您引用的表达式,可以通过阅读输出的汇编代码来观察。
不,默认情况下这种类型的优化不与浮点一起使用(除非优化器可以证明没有准确性丢失)。请参阅 Are floating point operations in C associative? 您 可以 让例如 gcc 使用 -fassociative-math
选项来执行此操作。 后果自负。