编译器是否优化通过指针进行的对琐碎函数的调用
Do compilers optimize away calls to trivial functions made through pointers?
假设我有一个函数需要一个函数指针:
int funct(double (*f)(double));
我给它传递了一个实际上什么都不做的函数:
double g(double a) { return 1.0;}
//...
funct(g);
编译器会优化对g
的调用吗?或者这还会有开销吗?如果它确实有开销,多少?是否足够让函数重载以同时接收函数指针和常数值?
GCC的较新版本(4.4及更高版本)可以使用选项-findirect-inlining
内联和优化已知函数指针。只有当GCC还知道使用指针的所有代码时,这才有效。
例如,C库函数qsort
将不会从该优化中获益。qsort的编译机器代码在库中,它需要一个函数指针,而编译器无法更改它。
然而,如果您有自己的qsort实现,并将其放在头文件中,或者使用了非常新的GCC链接时间优化功能,那么GCC将能够获取您的调用代码、指向的函数和qsort源代码,并将它们一起编译,针对您的数据类型和比较函数进行优化。
现在,只有当函数调用开销远大于函数本身时,这才是真正重要的。在一个什么都不做的函数的例子中,使用函数指针是一种严重的开销。在我的qsort比较示例中,函数指针调用也相当昂贵。但在其他一些应用程序(如Windows事件调度)中,这并不重要。
既然你在使用C++,你就应该学习模板。模板函数可以接受所谓的function object
,它只是一个实现operator()
的对象,它可以接受函数指针。传递函数对象将允许C++编译器内联和优化几乎所有涉及的代码。
任何现代编译器都可以(也将)轻松地优化通过函数指针进行的调用,当编译器在编译时知道指针指向何处时。
在您的特定示例中,在一般情况下,指针的值不能在编译时调用时预测,因为它是作为函数funct
的参数从外部传递的。在函数funct
本身小到可以内联的情况下,运行时参数会被删除,funct
的整个代码会在funct
的调用方上下文中"溶解"。指针的值在调用方上下文中是已知的,那么,编译器可以通过指针轻松地消除调用。
最后,如果函数funct
相对较重(意味着它没有内联),那么您可能希望编译器通过funct
内部的指针生成一个普通的运行时调用。在这种情况下也存在调用消除的可能性,因为编译器理论上可以为g
的每个编译时值生成单独版本的funct
。例如,如果你有
int funct(int x1, int x2, int x3, double (*f)(double));
其仅使用参数CCD_ 14 的两个可能自变量来调用
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);
没有函数指针参数并且内部直接调用CCD_ 15和CCD_。(当然,这种优化与函数指针没有任何联系,可以应用于任何只与一小部分固定参数一起使用的函数参数。事实上,这与C++模板类似。)但我不知道有任何编译器会这样做。
编译器可能不会对其进行优化,因为函数funct
可以接收指向不同函数的指针,而不仅仅是g
,而且它们不必来自同一编译单元(因此编译器不能假设它知道所有可能的调用)。
你需要对你的代码进行基准测试,看看你所说的优化是否有必要,如果有,那就去做吧。但除非funct
经常调用g
,否则我不认为这有什么关系。
- 如何用参数值调用函数(仅在运行时已知)
- 从python中调用C++函数并获取返回值
- 当使用通配符和null指针调用函数时,对输出的说明
- 从R调用C++函数并对其进行集成时出错
- 使用QTreeView,如何通过调用函数只突出显示特定的行/列
- 如何在qt中从另一个类调用函数
- 在 COUT 语句中使用 COUT 调用函数
- 如何从线程中的不同模块调用函数?
- C++从函数指针数组调用函数
- 当 A 在 for 循环中调用函数 B 时,如何计算函数 A 的空间复杂度?
- 如何在 C/C++ 中从外部库调用函数
- 如何使用运算符在同一行中多次调用函数
- 是否可以创建一个从不同类调用函数的线程?
- 无法为类成员调用函数
- 如何从另一个标头 c++ 调用函数
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- 如何只允许在调用函数 B 后调用函数 A?
- 我可以这样调用函数吗?
- 如何在 c++ 的类中递归调用函数方法?
- 为什么在指向对象的迭代器上调用函数不允许我更改对象本身?