c++ - C/C++编译器可以以任何方式内联C回调函数吗?

标签 c++ c optimization compiler-optimization inlining

给定一个将C-Functionpointer作为回调的典型函数,例如C-Stdlib qsort(),任何编译器都可以使用内联优化代码吗?我认为不可能,这是正确的吗?

int cmp(void* pa, void* pb) { /*...*/ }
int func() {
  int vec[1000];
  qsort(vec, 1000, sizeof(int), &cmp);
}

好的,qsort()是来自外部库的函数,但是我不认为LTO在这里也可以,对吗?

但是,如果我在同一编译单元中定义了my_qsort(),那么编译器可以内联吗?
int cmp(void* pa, void* pb) { /*...*/ }
void my_qsort(int* vec, int n, int sz, (void*)(void*,void*)) { /* ... */ }
int func() {
  int vec[1000];
  my_qsort(vec, 1000, sizeof(int), &cmp);
}

那有什么区别吗?我认为使用C函数指针作为回调是阻止编译器内联的因素。正确?

(我只是想确保我理解为什么我应该在C++中使用Functors)

最佳答案

不,至少在传统工具链的工作方式上,这是不可能的。传统的操作顺序是完成所有编译,然后完成链接。

要内联生成比较函数,编译器首先必须为内联qsort本身生成代码(因为qsort的每个实例通常将使用不同的比较函数)。但是,对于qsort之类的东西,通常甚至在开始考虑编写代码之前就已将其编译并放置在标准库中。编译代码时,qsort仅可用作目标文件。

这样,即使有机会做这样的事情,您也需​​要在链接器而不是编译器中构建内联功能。至少在理论上这是可能的,但绝对是不平凡的-至少在我看来,这几乎肯定比使用源代码要困难得多。它还需要在链接器中复制很多类似编译器的功能,并且可能需要在目标文件中添加大量额外的信息,以使链接器有足够的信息来使用它甚至可以尝试完成此工作。

编辑:也许我应该更详细一些,以免评论链变成一个完整的论点,而不仅仅是措辞。

传统上,链接器从根本上讲是一种非常简单的野兽。它从一个目标文件开始,该文件可以分为四个主要方面:

  • 从目标文件复制到正在生成的可执行文件的位集合(除非特别指示,否则保留不变)。
  • 对象文件包含的符号列表。
  • 对象文件未提供的使用的符号列表。
  • 需要写入地址的修正列表。

  • 然后,链接器开始匹配在一个文件中导出并在另一个文件中使用的符号。然后,它在一个或多个库中的目标文件中查找以解析更多符号。每次添加文件时,它也会添加所需符号的列表,并递归搜索其他满足要求的对象文件。

    当它找到提供所有符号的目标文件时,将每个符号的位集合复制到输出文件中,并在修正记录告诉它的位置,将分配给特定符号的相对地址写入(例如,称为printf,它找出在可执行文件中的哪个位置复制了组成printf的位,并用该地址填充您的调用)。在最近的合理情况下,可以从共享库中嵌入对共享对象/DLL的引用,而不是从库中复制位,然后将其留给加载程序以在运行时实际查找/加载该文件以提供实际的代码。一个符号。

    但是,尤其是,链接器传统上会忽略其复制的位块的实际内容。您可以(例如)完全合理地使用相同的链接器来处理许多不同处理器中的任何一个的代码。只要它们都使用相同的对象和可执行文件格式,就可以了。

    链接时间优化的确在某种程度上改变了这一点。显然,为了优化代码,我们需要某种在传统上认为是链接时发生的额外智能。有(至少)两种方法可以做到这一点:
  • 在链接器
  • 中建立了很多额外的智能
  • 将情报保留在编译器中,并使链接器调用它进行优化。

  • 两者都有例子-LLVM(一个明显的例子)几乎采用了前者。前端编译器发出LLVM代码,并且LLVM投入了大量的智能/工作来将其转换为优化的可执行文件。使用GIMPLE的gcc采取了后一种方法:GIMPLE记录基本上为链接器提供了足够的信息,可以将许多目标文件中的位反馈给编译器,让编译器对其进行优化,然后将结果反馈给链接器以实际复制到可执行文件中。

    我想您可能会提出某种哲学观点,说这两者在本质上是等效的-但我有点怀疑任何实现了这两者的人都会同意。

    现在,这两个事实中的任何一个都足以实现手头的优化,这是事实(也许无论如何)。就我个人而言,我怀疑是否有人为自己实现了这种优化。当您开始使用它时,qsortbsearch几乎是它将/通常将应用的两个合理的通用功能。对于大多数实际目的,这意味着您将仅出于qsort的目的执行优化。

    另一方面,如果所涉及的工具包括产生内联函数和链接时间优化的能力,那么我认为至少有合理的机会,您最终可能会由于某种偶然的情况而最终发生这种特定类型的优化,两者在一起的效果。

    至少从理论上讲,这意味着有可能发生。不过,还有更多需要考虑的问题:完全独立于手头的优化,许多编译器不会为递归函数生成内联代码。为了进行尝试,编译器必须首先将递归函数转换为迭代形式。在尾递归的情况下,这是很常见的-但是快速排序不是尾递归。几乎唯一的选择是qsort的实现,该实现不是递归的。这当然是可能的,但也确实很不寻常。

    因此,即使/如果工具链可以支持内联生成回调,则在qsort的情况下(我会承认,这是我亲自测试过的唯一情况)也可能不会。不管是好是坏,qsort几乎是这种函数中唯一常见的函数,它对它也没有太大的影响。

    关于c++ - C/C++编译器可以以任何方式内联C回调函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5290695/

    相关文章:

    c - 这个 if 语句在 C 中是如何工作的。我知道它的作用,但我不能把它分解成碎片

    c - 带有 C 语言函数的简单无限循环

    c++ - 如何优化路径列表中的目录列表?

    R 优化使用 stri_extract 的双循环

    c++ - 如何在类声明之外定义嵌套模板类的方法?

    c++ - 将字符串放入条件 (c++)

    c++ - 引导文件编译错误

    c - 如何在 C 中多次调用 sem_open?

    c++ - 如何缩短内部类私有(private)的返回类型中的嵌套命名空间?

    performance - 如何针对动态间隔调度优化这些 ocaml 函数?