假设我有一个函数double F(double x)
,让我们假设为了这个例子,调用F
是昂贵的。
假设我写了一个函数f
来计算F
的平方根:
double f(double x){
return sqrt(F(x));
}
在第三个函数sum
我计算f
和F
的总和:
double sum(double x){
return F(x) + f(x);
}
由于我想最小化对F
的调用,因此上述代码与例如
double sum_2(double x){
double y = F(x);
return y + sqrt(y);
}
但由于我很懒惰或愚蠢,或者想让我的代码尽可能清晰,我选择了第一个定义。
C / C ++编译器是否会优化我的代码,因为它意识到F(x)
的值可以重用来计算f(x)
,就像在sum_2
中一样?
非常感谢。
C / C ++编译器是否会通过认识到F(x)的值可以重用来计算f(x)来优化我的代码,就像在sum_2中一样?
也许。两种语言都不需要这样的优化,它们是否允许它取决于F()
实现的细节。一般来说,不同的编译器在这种情况下表现不同。
完全合理的是,编译器会将函数f()
内联到函数sum()
中,这将使它有机会认识到有两个调用F(x)
会导致相同的结果。在这种情况下,如果F()
没有副作用,那么可以想象编译器只会发出一次对F()
的调用,重用结果。
特定实现可以具有可以用于帮助编译器得出这样结论的扩展。但是,如果没有将这样的扩展应用于该问题,我认为编译器不太可能发出只执行一次F()
调用并重用结果的代码。
您所描述的内容称为memoization,一种(通常)运行时缓存的形式。虽然这可能在编译器中实现,但它通常不在C编译器中执行。
C ++确实有一个聪明的解决方法,使用STL,几年前详细的at this blog post;还有一个稍微更新的SO答案here。值得注意的是,通过这种方法,编译器不会“巧妙地”推断函数的多个相同结果将被重用,但效果大致相同。
有些语言(如Haskell)具有编译时记忆功能,但编译器体系结构与Clang或GCC / G ++完全不同。
许多编译器使用提示来确定是否可以重用先前函数调用的结果。一个经典的例子是:
for (int i=0; i < strlen(str); i++)
在没有对此进行优化的情况下,该循环的复杂度至少为O(n2),但在优化之后它可以是O(n)。
gcc,clang和许多其他人可以采取的提示是__attribute__((pure))
和__attribute__((const))
,它们被描述为here。例如,GNU strlen
被声明为纯函数。
GCC可以检测pure functions,并建议程序员哪些功能应该是纯粹的结婚。实际上,它会自动执行以下简单示例:
unsigned my_strlen(const char* str)
{
int i=0;
while (str[i])
++i;
return i;
}
unsigned word_len(const char *str)
{
for (unsigned i=0 ; i < my_strlen(str); ++i) {
if (str[i] == ' ')
return i;
}
return my_strlen(str);
}
你可以用qazxsw poi qazxsw poi for gcc。它在整个see the compilation result函数中只调用qazxsw poi一次。 Clang 7.0.0似乎没有执行此优化。
-O3 -fno-inline