繁体   English   中英

建议编译器有选择地内联函数调用

[英]Suggest to the compiler to selectively inline function calls

假设我有以下代码:

struct Foo {
  void helper() { ... }
  void fast_path() { ...; helper(); ... }
  void slow_path1() { ...; helper(); ... }
  void slow_path2() { ...; helper(); ... }
};

fast_path()方法fast_path()性能至关重要,因此应尽一切努力使其尽可能快。 slow_path1()slow_path2()方法不是性能关键。

根据我的理解,典型的编译器可能会查看此代码并决定不内联helper()如果它足够复杂,以减少总指令大小,因为helper()在多个方法函数之间共享。 如果慢路径方法不存在,那么相同的编译器可能会内联helper()

鉴于我们期望的性能特征,我们希望编译器内联对fast_path()内部的helper()的调用,但更喜欢编译器在slow_path1()slow_path2()的默认行为。

一种解决方法是使慢速路径函数定义和对fast_path()的调用存在于单独的编译单元中,以便编译器永远不会看到与fast_path()共享的helper()使用。 但是保持这种分离需要特别小心,不能通过编译器强制执行。 此外,文件(Foo.h,FooINLINES.cpp,现在还有Foo.cpp)的扩散是不可取的,并且额外的编译单元使可能只是标题库的构建变得复杂。

有没有更好的办法?

理想情况下,我想要一个新的do_not_inline_function_calls_inside_me c ++关键字,我可以这样使用:

  do_not_inline_function_calls_inside_me void slow_path1() { ... }
  do_not_inline_function_calls_inside_me void slow_path2() { ... }

或者,使用inline_function_calls_inside_me关键字,如下所示:

  inline_function_calls_inside_me void fast_path() { ... }

请注意,这些假设关键字装饰*_path*()方法,而不是helper()方法。

您可能具有这些性能需求的示例上下文是编程竞赛,其中每个参与者编写侦听类型A和B的稀疏全局数据广播的应用程序。当接收到类型B广播时,每个应用程序必须执行计算取决于先前广播的A类消息的顺序,并将计算结果提交给中央服务器。 每个B类广播的第一个正确响应者得分。 计算问题的性质可能允许对A类更新执行预计算; 快速做这些没有任何好处。

一般来说,你不应该试图比编译器更聪明。 现代编译器在决定如何内联函数方面做了很棒的工作,而人类在推理这个方面却出了名。

根据我的经验,您可以做的最好的事情是将所有相关功能作为inline函数放在同一个翻译单元中,这样编译器就可以看到它们的定义,并可以在它认为合适时内联它们。 然而,Levae最终决定是否将给定函数内联到编译器,并且非常谨慎地使用“强制内联”,除非您有证据表明它在给定情况下具有有益效果。

为了使编译器的工作更容易,您可以为其提供有关程序的其他信息。 在GCC和Clang中,您可以使用函数属性

struct Foo {
  void helper();
  void fast_path()  __attribute__ ((hot));
  void slow_path1() __attribute__ ((cold));
  void slow_path2() __attribute__ ((cold));
};

inline void Foo::helper()     { … }
inline void Foo::fast_path()  { … }
inline void Foo::slow_path1() { … }
inline void Foo::slow_path2() { … }

这将暗示编译器优化Foo::fast_path更积极地对速度和Foo::slow_path1Foo::slow_path2的小高速缓存足迹。 如果这些函数中的任何一个调用Foo::helper ,它可以根据具体情况决定是否内联它。 (有关注释的精确效果,请参阅链接手册中的文档。)

提示编译器的更好方法是给它实际的分析数据。 使用GCC,您可以使用-fprofile-generate选项编译程序。 这将使用收集配置文件统计信息的代码来检测二进制文件。 现在使用一组有代表性的输入运行您的程序。 这样做将使用收集的配置文件数据创建*.gcda文件。 现在-fprofile-use选项重新编译。 GCC将使用收集的配置文件信息来确定代码中的哪些路径很热,以及它们如何相互交互。 该技术称为轮廓引导优化 (PGO)。

当然,如果你担心这些事情,首先要确保你启用了适当的优化级别( -O2 )。 特别是模板繁重的C +代码(即,几乎所有使用标准库或Boost的代码)在编译时都会产生非常难看的机器代码而没有适当的优化。 还要考虑是否要将assert编译成代码( -DNDEBUG )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM