C/C++编译器是否可以内联C-Callback函数?

tow*_*owi 9 c c++ optimization inlining compiler-optimization

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

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

好的,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);
}
Run Code Online (Sandbox Code Playgroud)

这有什么不同吗?我认为使用C函数指针作为回调是阻止编译器内联的因素.正确?

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

Jer*_*fin 7

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

要内联生成比较函数,编译器首先必须为qsort内联生成代码(因为每个实例qsort都会使用不同的比较函数).qsort但是,在某些情况下,它通常是在您开始考虑编写代码之前编译并放置在标准库中.编译代码时,qsort只能作为目标文件使用.

因此,即使有机会做这样的事情,您需要在链接器而不是编译器中构建内联功能.至少在理论上这是可能的,但它确实是非平凡的 - 至少在我的估计中,它几乎肯定比使用源代码更困难.它还需要在链接器中复制相当多的类似编译器的功能,并且可能需要在目标文件中添加大量额外信息,以便为链接器提供足够的信息以便它甚至可以尝试完成工作.

编辑:也许我应该进一步详细说明,以免评论链变成一个完整的论点而不仅仅是措辞.

传统上,链接器基本上是一种相当简单的野兽.它从一个目标文件开始,可以分为四个主要内容:

  1. 要从目标文件复制到正在生成的可执行文件的位的集合(未更改,除非特别指示).
  2. 目标文件包含的符号列表.
  3. 目标文件未提供的符号列表.
  4. 需要写入地址的修正列表.

然后,链接器开始匹配在一个文件中导出的符号,并在另一个文件中使用.然后,它会查看库(或库)中的目标文件以解析更多符号.只要它在文件中添加,它还会添加所需符号列表,并递归搜索其他可满足这些符号的目标文件.

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

然而,特别地,链接器传统上不知道它正在复制的位块的实际内容.您可以(例如)非常合理地使用完全相同的链接器来处理许多不同处理器中的任何一个的代码.只要它们都使用相同的对象和可执行文件格式,就可以了.

链接时间优化确实至少在某种程度上改变了这一点.显然,为了优化代码,我们需要在传统上被认为是链接时间的某种额外智能.有(至少)两种方法可以做到这一点:

  1. 在链接器中构建了相当多的额外智能
  2. 保持编译器的智能,并让链接器调用它来进行优化.

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

我想你可能会提出某种哲学观点,认为这两者基本相同 - 但我怀疑任何实施两者的人都会同意.

现在,确实(可能,无论如何)其中任何一个都足以实现手头的优化.就个人而言,我怀疑是否有人为了自己的利益而实现了这种优化.当你到它,qsort并且bsearch几乎是仅有的两个相当常见功能,它会/通常会适用.对于大多数实际用途,这意味着您将专门为了实现优化qsort.

另一方面,如果所涉及的工具包括生成内联函数和链接时间优化的能力,那么我认为至少有一个合理的可能性,你可以最终将这种特殊类型的优化作为一个或多或少的意外方面发生 - 两个人聚在一起的效果.

至少在理论上,这意味着它可能发生.还有一点要考虑:完全独立于手头的优化,许多编译器不会为递归函数生成内联代码.为了尝试,编译器必须首先将递归函数转换为迭代形式.在尾递归的情况下这是相当常见的 - 但快速排序不是尾递归.几乎唯一的替代方案是qsort开始时不是递归的实现.这当然是可能的,但同样肯定是不寻常的.

因此,即使/如果工具链可以支持内联生成回调,它可能也不会qsort(我承认,这是我个人测试的唯一情况).然而,无论好坏,qsort几乎是这种类型的唯一功能,它通常足以让它变得更重要.