我正在构建一个运行时系统,该系统允许程序员指定在特定点调用的回调。我正在使用 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 不会内联。当我使用性能事件运行系统时,我可以看到该函数没有被内联:
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
的全部要点是拥有一个通用类型,可以保存某个签名的任意可调用对象,同时允许通过通用接口(interface),无论可调用对象实际发生的是什么类型的事情。因此,如果您考虑一下,std::function
本质上需要某种间接寻址。调用 std::function 需要运行哪些代码不仅取决于类型,还取决于 std::function 的特定值。这使得 std::function (至少是对存储的可调用对象的调用)本质上不可内联。为调用回调的函数生成的代码必须能够处理您可能向其抛出的任何 std::function 。编译器可能提供诸如 std::function 内联之类的东西的唯一方法是,如果它能够以某种方式找出调用回调的函数大多数时候只与std::function
对象保存特定值,然后生成调用该特定情况回调的函数的克隆。这要么需要一个几乎不切实际的洞察力编译器才能实现,要么需要在编译器中专门为 std::function
硬连线很多魔法。理论上并不是完全不可能。但我从未见过任何编译器实际上能够做这样的事情。根据我的经验,优化器并不能真正看透 std::function
。我不认为这种情况会很快改变,因为要获得任何有意义的优化似乎都需要付出巨大的努力才能获得相当值得怀疑的好处。 std::function
一开始只是重型机械。您只需为在那里使用的东西付费即可。如果你付不起代价,就不要使用std::function
…
关于c++ - 在库中强制内联 C++17 中的回调 (lambda),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56121894/