2016-01-19 20 views
1

考虑下面的代码段示出了一些简单的算术运算gcc是否以代数方式优化C++代码,如果是的话,程度如何?

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)); 
+1

请提供指向C/C++语言规范的链接。在此之前,只有两种不同的**语言C和C++。选一个!你的问题对于SO来说太广泛了。请自己做一些研究。 gcc邮件列表可能是一个好的开始。或者你只是阅读源代码。 – Olaf

+1

您可以随时编译并检查程序集以查看它的功能。 – NathanOliver

+1

小心!当你开始考虑整数溢出时,这种代数重排通常会中断。我无法想到这个特定表达式的反例,但有很多表达式会打破它。 –

回答

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! 

不保证该线是评估的确切数量相同。没有这种保证,GCC不能优化这些结构。

此外,操作顺序可能很重要。例如:

int x = y; 
int z = (y/16) * 16; 

assert(x == z); //Will only be true if y is a whole multiple of 16 

代数上,这两行应该是等价的,对吗?但如果yint,它实际上会做的是使x等于“y四舍五入到16的较低整数倍”。有时,这就是预期的行为(就像你是字节对齐一样)。其他时候,这是一个错误。重要的是,两者都是有效的计算机代码,并且两者都可以根据具体情况而变得有用,并且如果GCC围绕这些结构进行优化,则会妨碍程序员对代码进行代理。

+0

在nathan oliver的建议下,我整理了上面的整数示例,并生成完全相同的程序集。在这种情况下,gcc通过重构'result = c *(a + b)+ d *(a + b)+ e;'result'=(c + d)*(a + b)+ e; '或者也许是我不知道的其他方式。然而,关于浮点示例,你是正确的。尽管如此,我不了解组装,但无法弄清楚发生了什么。 – silvergasp

0

是的,包含gcc的优化器会优化这种类型。不过,不一定是你引用的表达。您必须考虑到可能的溢出。例如,(a + a) - a可能会被优化为a。可能的优化的另一个示例是a*a*a*atemp = a*a; (temp)*(temp)

通过读取输出汇编代码可以观察给定的编译器是否优化了引用的表达式。不,这种类型的优化默认情况下不会与浮点一起使用(除非优化器可以证明没有准确性丢失)。见Are floating point operations in C associative?可以让例如gcc做-fassociative-math这个选项。 在你自己的危险。