在库中的 C++17 中强制内联回调 (lambda)

Forcing inlining of callback (lambda) in C++17 in library

本文关键字:回调 lambda C++17      更新时间:2023-10-16

我正在构建一个运行时系统,它允许程序员指定在特定点调用的回调。我正在使用clang 7.0.1/-std=c++17。回调通过将 lambda 存储为std::function在运行时注册。当运行时稍后调用std::function回调时,它会传递 6 个参数(考虑到运行时的通用性,这是必要的)。请注意,std::function是在应用程序中创建的,但由单独编译的静态链接库使用。但是,我使用的是 LTO(通过-flto和 LLD 7.0.1),所以我希望它仍然能够进行此优化。我是其中一些东西的新手,所以希望这是可能的。

当我使用-O3进行编译并在调用函数声明上指定__attribute__((flatten))时,lambda 不是内联的。当我使用 perf 事件运行系统时,我可以看到该函数没有内联:

return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
mov    -0x90(%rbp),%rdi  
lea    -0x48(%rbp),%rsi  
mov    %rbx,%rdx         
mov    %r15,%rbx         
callq  *0x180(%r15)      
...

这个调用花费了不小的时间,它似乎是内联的;总共只有几个调用站点。我以前当然见过内联的 lambda,但我不确定我使用函子(通过std::function)的方法是否以某种方式取消了内联的资格。

强制内联可能吗?如果这里需要更多信息,请告诉我。

编辑:感谢所有非常有用的信息。我现在意识到,我设置运行时的方式并没有给编译器内联回调的机会。这些评论清楚地说明了为什么会这样。有一些关于替代方法的暗示可能是内联的。鉴于 1) 我控制应用程序和运行时源(以及编程模型/API);2)我正在同时编译库和应用程序(甚至可以使它们成为一个统一的构建过程),我是否可以在这里采取其他方法,可能会允许内联发生?也许模板和 lambda(不是std::functions)?我是这个领域的新手,如果有人对如何有效地为编译器提供它需要内联的内容有想法,我会全力以赴。最坏的情况是,如果这开辟了任何可能性,我甚至可以为每个应用程序构建库的自定义版本(作为概念证明)......

std::function

的全部意义在于拥有一个通用类型,该类型可以保存某个签名的任意可调用对象,同时允许通过公共接口调用该任意可调用对象,而不管可调用对象实际是哪种类型。因此,如果你考虑一下,std::function本质上需要某种间接。调用std::function需要运行哪些代码不仅取决于类型,还取决于std::function的特定值。这使得std::function(至少是对存储的可调用对象的调用)本质上不可内联。为调用回调的函数生成的代码必须能够处理您可能抛出的任何std::function。编译器可能为std::function提供内联的唯一方法是,如果它以某种方式能够确定调用回调的函数大多数时候只会与std::function持有特定值的对象一起使用,然后生成调用该特定情况回调的函数的克隆。这要么需要一个几乎不切实际的千里眼编译器才能达到,要么需要很多魔法硬连接到编译器中,只是为了std::function而专门用于。从理论上讲,这并非完全不可能。但我从未见过任何编译器能够做这样的事情。根据我的经验,优化器只是无法真正看透std::function。而且我预计这种情况不会很快改变,因为在那里进行任何有意义的优化似乎都需要付出巨大的努力才能获得相当可疑的好处。std::function一开始只是重型机械。您只需为在那里使用的内容付费。如果你付不起代价,就不要用std::function...