假设我有一个带函数指针的函数:
int funct(double (*f)(double));
我传给了一个实际上没有做任何事情的函数:
double g(double a) { return 1.0;}
//...
funct(g);
编译器会优化对g
的调用吗?或者这还有开销吗?如果确实有开销,多少钱?足够值得重载函数以接收函数指针和常量值吗?
答案 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
,否则我不希望这一点很重要。