【发布时间】:2019-05-14 01:38:34
【问题描述】:
我正在构建一个运行时系统,允许程序员指定在特定点调用的回调。我正在使用 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)是否会以某种方式取消内联。
是否可以强制内联?如果需要更多信息,请告诉我。
编辑:
感谢所有非常有用的信息。我现在意识到我设置运行时的方式并没有让编译器有机会内联回调。 cmets 清楚地说明了为什么会这样。有一些暗示可能是内联的替代方法。鉴于 1)我同时控制应用程序和运行时源(以及编程模型/API); 2)我同时编译库和应用程序(甚至可以使它们成为一个统一的构建过程),我可以在这里采用可能允许内联发生的替代方法吗?也许是模板和 lambdas(不是std::functions)?我是这个领域的新手,如果有人对如何有效地为编译器提供内联所需的内容有想法,我会全力以赴。在最坏的情况下,我什至可以为每个应用程序构建一个自定义版本的库(作为概念证明),如果这会带来任何可能性......
【问题讨论】:
-
"看起来应该是可内联的" 你怎么看?您的描述强烈表明 lambda 的定义点和恰好包含该 lambda 的
std::function对象的最终调用点相距甚远(如在不同的翻译单元中)。那么为什么你会期望内联呢?链接时优化不是魔法;它只能做这么多。 -
你有没有测量过这是否是你的瓶颈?
-
编译器使注册的回调可内联的唯一方法是证明它是唯一将被注册的回调。 LTO 通常无法做到这一点。它不是一种全程序优化技术。
-
@Swordfish 我看到使用 perf record -e Cycles:pp 初始化和调用函子是我的关键路径,也是我现在可以优化的前三件事之一