C编程。为什么函数指针会破坏优化

C programming. Why do function pointers destroy optimization

我与使用函数指针的 C 编程建立了 love/hate 关系。我主要使用闪存 space 有限的微控制器,所以显然我对使用优化很感兴趣。一方面,函数指针让我的生活变得如此轻松,因为我可以编写一个硬件可移植软件模块,用户可以在其中提供函数回调来执行与硬件相关的事情(例如:用户回调以将字节写入串行端口)。这样,我可以编写 95% 独立于平台的代码,然后可以快速移植到新硬件。

另一方面,我注意到许多编译器在看到任何函数指针的使用时都会将优化抛出 window。例如,我写了一个相当通用的函数,它接受一个枚举类型作为参数,然后有一个巨大的 switch/case 语句用于每个枚举类型选择来配置寄存器等,这取决于用户传递给 function.I我用一个常量文字调用这个函数一次,即一些永远不应该改变的东西。构建的二进制文件似乎包含用于处理每个 switch/case 选择的操作码,尽管除了一个案例选择之外的所有选择都应被视为 "dead code." 我使用了不同的优化设置,但为了获得最小的二进制我必须完全注释掉每个 "case" 块,除了我需要的那个,这减少了大约 1k 字节的闪存利用率。如果我不使用函数指针,优化这段代码是没有问题的。

奇怪的是,我从来没有在我的代码中使用函数指针和函数的原型。对于我使用的函数指针原型,指针在启动时建立一次指向静态函数,然后再也不会(另一个常量文字赋值)。我知道我可能可以在所有地方使用 #defines 来实现相同的设计目标,但令我恼火的是这些工具无法根据我的代码解释什么是 possible/impossible。

我可以看到你有一个函数指针的情况,并且一些你无法预测的动态事物会执行该函数(即一个人在终端中键入内容并且你事先不知道参数是什么将会)。

为什么编译器在使用函数指针时难以优化,即使是以可预测的方式进行优化,这有什么充分的理由吗?

没有看到你的代码,我无法得出任何明确的结论,但这是我根据自己的经验所知道的。

即使函数是静态的并且只调用一次,指针也会在运行时初始化。编译器无法优化地址直到运行时才知道的函数指针。

当函数存在于其他翻译单元时,也无法优化掉函数指针;如果函数在翻译单元之外,它不知道函数在哪里,只知道它们已被声明;处理它是链接器的工作。我相信 LTO 理论上应该可以处理这个问题,但我不确定是否有任何实现可以做到这一点。

但是,如果某些编译器知道所有地址的位置,并且函数指针在编译时初始化,则可以优化函数指针(想到 MSVC)。

基本问题是单独编译——当您使用一种语言(例如 C),其中不同的源文件分别编译,然后 link由单独的 linker 编辑在一起时,编译器优化器在运行时不知道其他编译单元中的任何内容(那些编译单元甚至可能还没有编写!),因此它无法知道指针未被修改 and/or不使用任何其他参数调用函数。

一个可能的解决方案是 link 时间优化——如果您的开发套件支持的话。

另一种方法是将所有内容强制放入一个编译单元并使所有内容 static 以便编译器知道没有其他编译单元可以引用它。这可能会让优化器做得更好。