Caching 内联和指令缓存命中率和抖动

Caching 内联和指令缓存命中率和抖动,caching,memory,inline,executable,cpu-architecture,Caching,Memory,Inline,Executable,Cpu Architecture,在本文中,它指出内联的缺点是: 3) 过多的内联也会降低指令缓存命中率,从而降低从缓存到主内存的指令提取速度 内联如何影响指令缓存命中率 6) 内联函数可能会导致抖动,因为内联可能会增加二进制可执行文件的大小。内存中的抖动会导致计算机性能下降 内联如何增加二进制可执行文件的大小?只是它增加了代码基长度吗?此外,我不清楚为什么拥有一个更大的二进制可执行文件会导致抖动,因为这两个文件似乎没有链接。假设您有一个100条指令长的函数,每次调用它都需要10条指令 这意味着对于10次调用,它将使用100+1

在本文中,它指出内联的缺点是:

3) 过多的内联也会降低指令缓存命中率,从而降低从缓存到主内存的指令提取速度

内联如何影响指令缓存命中率

6) 内联函数可能会导致抖动,因为内联可能会增加二进制可执行文件的大小。内存中的抖动会导致计算机性能下降


内联如何增加二进制可执行文件的大小?只是它增加了代码基长度吗?此外,我不清楚为什么拥有一个更大的二进制可执行文件会导致抖动,因为这两个文件似乎没有链接。

假设您有一个100条指令长的函数,每次调用它都需要10条指令

这意味着对于10次调用,它将使用100+10*10=200条二进制指令

现在,让我们说它的内联在它使用的任何地方。在二进制文件中使用100*10=1000条指令

因此,对于第3点,这意味着它将在指令缓存中占用更多的空间(内联函数的不同调用在i-cache中不会“共享”)


对于第6点,您的总二进制大小现在更大了,并且更大的二进制大小可能会导致抖动。一般来说,内联倾向于增加发出的代码大小,因为调用站点被更大的代码片段替换。因此,可能需要更多的内存空间来保存代码,这可能会导致抖动。我将更详细地讨论这个问题

内联如何影响指令缓存命中率

内联对性能的影响通常很难在不实际运行代码和测量其性能的情况下进行静态描述

是的,内联可能会影响代码大小,通常会使发出的本机代码变大。让我们考虑以下情况:

  • 在特定时间段内执行的代码在这两种情况下(有或没有内联)都符合内存层次结构的特定级别(比如L1I)。因此,该特定级别的性能不会改变
  • 在没有内联的情况下,在特定时间段内执行的代码适合内存层次结构的特定级别,但不适合内联。这对性能的影响取决于执行对象的位置。本质上,如果最热的代码首先出现在该级别的内存中,那么该级别的未命中率可能会略微增加。现代处理器的特性,如推测执行、无序执行、预取,可以隐藏或减少额外未命中的惩罚。需要注意的是,内联确实改善了代码的局部性,这可能会导致性能上的净正imapct,尽管代码大小增加了。当调用站点内联的代码频繁执行时,尤其如此。部分内联技术已经发展到只内联函数中被认为是热的部分
  • 在这两种情况下,在特定时间段内执行的代码不适合内存层次结构的特定级别。因此,该特定级别的性能不会改变
此外,我不清楚为什么要有一个更大的二进制可执行文件 文件可能会导致颠簸,因为这两个文件似乎没有链接


考虑资源受限系统上的主内存级别。即使代码大小仅增加5%,也会导致主存出现抖动,从而导致性能显著下降。在其他资源丰富的系统(台式机、工作站、服务器)上,只有当热指令的总大小太大,无法容纳在一个或多个缓存中时,才会在缓存中发生抖动。

如果编译器尽可能内联所有内容,大多数功能都会非常庞大。(虽然您可能只有一个巨大的
main
函数调用库函数,但在最极端的情况下,程序中的所有函数都将内联到
main

想象一下,如果一切都是一个宏而不是一个函数,那么它将在您使用它的任何地方完全展开。这是内联的源代码级版本


大多数函数都有多个调用站点。调用函数的代码大小随参数的数量略有增加,但与中到大型函数相比,通常非常小。因此,在所有调用站点内联一个大型函数将增加代码的总大小,降低I-cache命中率

但是现在,通常编写大量的小包装器/助手函数,尤其是C++。小函数的独立版本的代码通常不会比调用它所需的代码大多少,特别是当您包含函数调用的副作用(如关闭寄存器)时。内联小函数通常可以节省代码大小,特别是在内联后可以进行进一步优化时。(例如,函数计算的内容与函数外的代码也计算的内容相同,因此是可能的)

因此,对于编译器来说,是否内联到任何特定调用站点的决定应该基于被调用函数的大小,可能还取决于它是否在循环中被调用。(如果调用站点运行得更频繁,优化掉调用/ret开销会更有价值。)概要文件引导优化可以帮助编译器做出更好的决策,方法是在热函数上“花费”更多的代码大小,在冷函数中节省代码大小(例如,许多函数在程序的生命周期内只运行一次,而一些热门函数占用了大部分时间)

如果编译器没有关于何时内联的好的启发式方法,或者您重写它们太过激进,那么是的,I-cache未命中就是结果

  mov eci, 10
  mov eax, 0
.top:
  add eax, eci
  dec eci
  jnz .top