TL;DR:许多资料都提到,过度内联函数有时会因为代码膨胀或其他因素而损害应用程序性能。有没有一个实际的程序示例可以以可衡量的方式证明这一点?
记住,微基准测试的使命是放大程序性能的某些方面。正因如此,任何人都可以轻松生成一个微基准测试,让任何特定问题看起来都像是大问题。// Rico Mariani 的性能花絮
我接触过的许多程序员都认为函数内联对应用程序性能绝对有益。我经常审查的 C/C++ 代码中,函数内联inline
(或类似的)关键字被随意地应用于函数,无论其大小、用途、热度或位置如何。
很多情况下,这种奇怪的习惯(这里称之为“内联病” )对整体性能并无害:现代编译器对于哪些代码需要内联有着很好的判断能力,而且很少有代码会因为热度高到让(非)内联产生任何影响。然而,它往往会对最终的软件设计造成损害:更多内容最终会堆积在头文件中,文件不再能够独立编译,等等。
虽然很容易证明,在没有连续基准测试的情况下应用随机内联不会对最终性能产生任何可衡量的差异,但我正在寻找一个极端的例子,强制执行该问题会严格损害性能。
微基准测试就足够了;虽然它无法证明内联对实际应用的任何影响,但它应该能够证明,盲目地强制执行内联并非无条件的好主意。这实际上是几乎所有代码优化过程背后的想法:它可能有帮助,也可能有害,有时甚至没有区别。
此类示例微基准测试的一些要求。
- 它应该是一个相当短的程序,最好是用 C 或 C++ 编写的;其他可以强制内联的语言也欢迎。
- 它不必是执行任何有用操作的程序,它可能会做一些“愚蠢”的事情只是为了加载/强调底层硬件。
- 应该支持两种编译模式:强制内联和禁用内联。可以使用任何技术来实现:条件编译来重新定义内联注解,编译器后端标志来控制内联等等。
- 无论在哪一种模式下进行编译,它都应该格式良好并表现出相同的明确定义的行为。
- 它应该包含至少两个函数,一个调用另一个函数,目的是影响其中至少一个函数的内联。
- 它可能包含任何保证/强制函数内联的技术。例如,可以使用标准
inline
关键字或编译器特定的扩展(__forceinline
等__attribute__ ((always_inline))
)来指示编译器执行此操作,无论其判断如何。 - 运行时,可以轻松报告性能(延迟、执行时间或类似指标)。它可以仅使用
time a.out
,也可以在受影响的代码周围内部调用计时工具。 - 最后,当由特定版本中的至少一个特定编译器进行编译并在至少一个目标系统上运行时,所得到的程序的两个变体表现出统计上的显著差异,并且强制内联构建比非内联构建更慢。
我确实意识到性能高度依赖于主机参数;在一台机器上较慢的程序在另一台机器上可能会变得同样快甚至更快。但我正在寻找一种最坏的情况,即不受限制的内联显然会适得其反。
理想情况下,不影响内联的其他编译器后端选项(例如整体优化级别等)对于两个构建应该是相同的,以排除可观察到的差异由它们解释而不是由应用/跳过的内联解释的可能性。
我对此类程序的起点有一个想法,但我需要进一步的帮助来充实它:
- 内部函数非常庞大,几乎无法放入 CPU 的指令缓存中。
- 外部函数足够大,因此,如果将内部函数强制内联到其中,则生成的代码部分将大于 CPU 的指令缓存。
- 程序的控制流以这样的方式组织:当所有内容都内联时,它会经历更高频率的指令缓存未命中、缓存刷新或类似事件,如果不强制内联,则不会发生这些事件。