考虑到您只是在尝试优化速度,在决定是否内联函数时,有什么好的启发式方法?显然代码大小应该很重要,但是当(例如)gcc或icc确定是否内联函数调用时,是否还有其他因素?该地区是否有重要的学术工作?
答案 0 :(得分:8)
JIT编译器和运行时类加载的语言有其他权衡,因为虚拟方法不是静态知道的,但JIT可以收集运行时分析信息,例如方法调用频率:
Design, Implementation, and Evaluation of Optimizations in a Just-in-Time Compiler(适用于Java)讨论静态方法和动态加载类的方法内联及其对性能的改进。
Practicing JUDO: Java Under Dynamic Optimizations声称他们的“内联策略基于代码大小和分析信息。如果方法条目的执行频率低于某个阈值,则该方法不会内联,因为它是为了避免代码爆炸,我们没有内联一个字节码大小超过25个字节的方法......为了避免沿着深度调用链内联,内联在调用时累积的内联字节码大小停止链超过40个字节。“虽然它们具有运行时分析信息(方法调用频率),但它们仍然小心避免内联大函数或函数链以防止膨胀。
A search on Google Scholar揭示了一些论文,例如
A search on Google Books揭示了很多关于各种情境中函数内联的论文或章节的书籍。
The Compiler Design Handbook: Optimizations and Machine Code Generation有一章关于编译器设计中的统计和机器学习技术,可以通过启发式设置各种参数,分析结果。本章参考了Vaswani等人的论文Microarchitecture Sensitive Empirical Models for Compiler Optimizations,他们提出了“使用经验模型” 用于构建用于编译器优化的微体系结构敏感模型的技术“。
(其他一些书谈论程序员的观点,例如C++ for Game Programmers,它讨论了过于频繁地内联函数的危险以及内联和宏之间的差异。编译器经常忽略程序员的内联请求,如果他们可以确定他们会弊大于利;这可以用宏作为最后的手段来覆盖。)
答案 1 :(得分:0)
函数调用意味着一些额外的代码(函数序言,设置新的堆栈帧,以及函数结尾,它被清理)。如果你的编译器发现函数代码与序言和结尾相比较小,那么它可以决定进行实际调用是不值得的,并且会内联函数。
我看到调用函数而不是内联函数的唯一好处是与大小相关。我想内联函数然后展开循环可能会导致显着的大小增加。
答案 2 :(得分:0)
据我所知,函数大小是编译器用于确定内联的唯一因素。但是,如果您进行配置文件引导优化(PGO),我相信编译器能够使用其他变量,例如呼叫数/呼叫建立时间。
答案 3 :(得分:0)
在.NET中主要基于大小。以编译的字节为单位测量父函数和子函数的大小。然后测量组合函数的大小。如果组合函数较小,那么内联是个好主意。
这样做的原因是可以将尽可能多的代码插入CPU的缓存中。缓存未命中比现代CPU中的函数调用要昂贵得多。