代码之家  ›  专栏  ›  技术社区  ›  Mike

对于内联函数,什么是好的启发式方法?

  •  6
  • Mike  · 技术社区  · 15 年前

    考虑到您只是在尝试优化速度,对于决定是否内联函数,什么是好的启发式方法?显然,代码大小应该很重要,但是当(比如)gcc或icc决定是否内联函数调用时,是否有其他通常使用的因素?这方面有什么重要的学术工作吗?

    4 回复  |  直到 15 年前
        1
  •  8
  •   Jared Updike    15 年前

    维基百科有 a few 关于这一点的段落,底部有一些链接:

    • 除了内存大小和缓存问题外, another consideration is register pressure . 从编译器的角度来看,“内联过程中添加的变量可能会消耗额外的寄存器,而在寄存器压力已经很高的区域,这可能会强制溢出,从而导致额外的RAM访问。”

    使用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 揭示了相当多的书籍与论文或章节的功能在不同的上下文中嵌入。

        2
  •  0
  •   zneak    15 年前

    函数调用意味着一些额外的代码(函数序言,在这里设置了新的堆栈帧,函数尾声,在那里清除了它)。如果编译器发现函数代码比序言和尾声小,那么它可以决定不值得进行实际调用,并将内联函数。

    我看到调用函数而不是内嵌函数的唯一好处是与大小相关。我想,在一个函数中嵌入一个函数,然后展开一个循环,可能会导致显著的大小增加。

        3
  •  0
  •   Anycorn    15 年前

    据我所见,函数大小是用于确定内联的唯一因子编译器。但是,如果您执行配置文件引导优化(PGO),我相信编译器能够使用其他变量,例如调用数/调用设置时间。

        4
  •  0
  •   Jonathan Allen    15 年前

    在.NET中,主要是基于大小。以编译字节为单位测量父函数和子函数的大小。然后测量组合函数的大小。如果组合函数更小,那么内联是一个好主意。

    这样做的原因是使尽可能多的代码插入CPU的缓存成为可能。缓存未命中比现代CPU中的函数调用昂贵得多。