编译器是否优化通过指针进行的对琐碎函数的调用

Do compilers optimize away calls to trivial functions made through pointers?

本文关键字:调用 函数 优化 是否 指针 编译器      更新时间:2023-10-16

假设我有一个函数需要一个函数指针:

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,否则我不认为这有什么关系。