编译器是否优化了对通过指针进行的简单函数的调用?

时间:2011-06-07 00:17:21

标签: c++ optimization function-pointers

假设我有一个带函数指针的函数:

int funct(double (*f)(double));

我传给了一个实际上没有做任何事情的函数:

double g(double a) { return 1.0;}
//...
funct(g);

编译器会优化对g的调用吗?或者这还有开销吗?如果确实有开销,多少钱?足够值得重载函数以接收函数指针和常量值吗?

3 个答案:

答案 0 :(得分:17)

较新版本的GCC(4.4及更高版本)可以使用选项-findirect-inlining内联和优化已知函数指针。这只适用于GCC也知道使用指针的所有代码。

例如,C库函数qsort从此优化中受益。 qsort的编译机器代码在库中,它需要一个函数指针,编译器不能改变它。

但是,如果您有自己的qsort实现并将其放在头文件中,或者您使用了新的GCC链接时优化功能,那么GCC 能够接听您的呼叫代码,指向的函数和qsort源代码,并将它们编译在一起,针对您的数据类型和比较函数进行了优化。

现在,唯一真正重要的是函数调用开销远大于函数本身。在你的函数示例中没有做任何事情,使用函数指针是一个严重的开销。在我的qsort比较示例中,函数指针调用也非常昂贵。但在Windows事件调度等其他应用程序中,它几乎不重要。

由于您使用的是C ++,因此您应该学习模板。模板函数可以接受所谓的function object,它只是一个实现operator() 的对象,它可以接受函数指针。传递函数对象将允许C ++编译器内联并优化几乎所有涉及的代码。

答案 1 :(得分:2)

当编译器知道指针在编译时指向的位置时,任何现代编译器都可以(并且将会)轻松优化通过函数指针进行的调用(即内联)调用。

在您的具体示例中,一般情况下,在调用时编译时无法预测指针的值,因为它是作为函数funct的参数从外部传递的。在函数funct本身足够小以便内联的情况下,运行时参数被消除,funct的整个代码在funct调用者上下文中“解散”。在调用者上下文中已知指针的值,然后编译器可以通过指针轻松地消除调用。

最后,如果函数funct相对较重(意味着它没有内联),那么您应该期望编译器通过{{1}内部的指针生成普通的运行时调用。 }。在这种情况下也存在消除调用的可能性,因为编译器理论上可以为funct的每个编译时值生成单独的funct版本。例如,如果你有

g

仅使用参数int funct(int x1, int x2, int x3, double (*f)(double));

的两个可能参数调用
f

然后编译器可以将它“缩减”为两个函数

funct(rand(), rand(), rand(), g1);
...
funct(rand(), rand(), rand(), g2);

没有函数指针参数,并且直接调用内部的int funct_g1(int x1, int x2, int x3); int funct_g2(int x1, int x2, int x3); g1。 (当然,这种优化与函数指针没有任何关系,可以应用于任何只与一小组固定参数一起使用的函数参数。事实上,这与C ++模板类似。)但我是不知道任何编译器会做那样的事情。

答案 2 :(得分:1)

编译器可能不会优化它,因为函数funct可以接收指向不同函数的指针,而不仅仅是g,并且它们不必来自同一个编译单元(因此编译器不能假设它知道所有可能的调用。)

您需要对代码进行基准测试,以确定您所讨论的优化是否必要,如果是 - 只需执行此操作即可。但除非funct多次调用g,否则我不希望这一点很重要。