什么可以导致程序第二次运行得更快?

时间:2011-09-26 21:10:02

标签: windows performance delphi caching

我在编写测试代码时注意到的一点是,长时间运行的操作在程序运行第一次时比在后续运行时运行更长,有时候是10倍或者更多。显然这里存在某种冷缓存/暖缓存问题,但我似乎无法弄清楚它是什么。

这不是CPU缓存,因为这些长时间运行的操作往往是我提供大量数据的循环,并且它们应该在第一次迭代后完全加载。 (另外,卸载和重新加载程序应清除缓存。)

此外,它不是光盘缓存。我已经通过预先从光盘加载所有数据并在之后处理它来排除这种情况,这是实际的CPU绑定数据处理进展缓慢。

那么什么可能导致我的程序在第一次运行时运行缓慢,但是如果我关闭它并再次运行它会更快地运行?我在几个不同的程序中看到了这一点,这些程序做了很多不同的事情,所以它似乎是一个普遍的问题。

编辑:为了澄清,我正在使用Delphi编写,但我并不认为这是特定于Delphi的问题。但这意味着无论问题是什么,它都与JIT问题,垃圾收集问题或托管代码带来的任何其他包袱无关。我不是在处理网络连接。这是纯CPU绑定处理。

一个例子:脚本编译器。它运行如下:

  • 从光盘
  • 将整个文件加载到内存中
  • 将整个文件刻录成令牌队列
  • 将队列解析为树
  • 在树上运行codegen以生成字节码

如果我将光盘中的所有内容从光盘加载到内存后输入一个巨大的脚本文件(~100k行),则第一次运行时,lex步骤大约需要15秒,后续运行需要2秒。 (是的,我知道这仍然是一段很长的时间。我正在努力......)我想知道这种减速的来源以及我能做些什么。

9 个答案:

答案 0 :(得分:13)

尝试三件事:

  • 在采样分析器中运行它,包括“冷”运行(重启后的第一件事)。通常应该足够了。
  • 检查内存使用情况,它是否会变得如此之高(甚至是短暂的)操作系统必须将内容交换出内存以便为您的应用腾出空间?仅这一点就可以解释你所看到的。另请查看启动应用时的可用内存量。
  • 启用系统性能工具并检查I / O计数器或文件访问,并确保在FileMon / Process Explorer下您没有忘记的某些文件或网络访问(剩余日志/测试代码)< / LI>

答案 1 :(得分:5)

甚至(特别是)对于非常小的命令行程序,问题可能是加载进程所需的时间,链接到动态链接的库等。我相信现代操作系统避免重复大量的这项工作,如果同一个程序一次运行两次,或重复运行。

我也不会轻易解除CPU缓存。 0级缓存与内部循环非常相关,但对于同一应用程序的第二次运行则更少。在我的便宜的Athlon 2 X4 645系统上,每个核心有64K + 64K(数据+指令)0级缓存 - 不是很大的内存。 1级缓存是每个核心IIRC 512K,因此不太可能因为启动程序的新运行所需的O / S代码,调用操作系统服务和标准库等而无法完成无关紧要.2级缓存(在拥有它的CPU上 - 我的Athlon 2没有,IIRC)仍然更大,并且主板/芯片组可能会提供更高级别和更大的缓存。

至少还有一种其他类型的缓存 - 分支预测表。虽然我认为他们会比0级缓存更快地变得无关紧要。

我通常发现单元测试程序第一次运行速度慢很多倍。但是,程序越大越复杂,效果就越小。

一段时间以来,应用程序的性能通常被认为是不确定的。虽然不是严格来说,但性能是由许多难以预测的因素决定的,因此它是一个很好的模型。例如,如果CPU有点温暖,可以降低时钟速度以防止过热。并且温度在芯片的不同部分变化,并且以复杂的方式在芯片上传导变化。随着时钟速度的变化和不同代码片段的不同需求改变了温度变化的模式,混沌(如混沌理论)行为有明显的潜在可能性。

在某些平台上,如果程序的第一次运行得到处理器运行,如果它是“快速”(而不是冷却/安静)模式,我不会感到惊讶,这意味着第二次运行的开始受益从速度提升到结束。但是,这将是一个棘手的问题 - 它必须是一个CPU密集型程序,如果你的冷却不充分,处理器可能会再次减速以避免过热。

答案 2 :(得分:4)

我经常遇到相反的情况:对于计算强化的工作(如果反病毒不起作用),我在呼叫之间只有5-10%的差异。例如,为我们的框架运行的6,000,000个回归测试具有非常恒定的运行时间,并且它是非常耗费磁盘和CPU的工作。

我真的不相信CPU缓存或流水线/分支预测问题,因为处理后的数据和代码似乎都是一致的,就像你写的那样。如果反病毒已关闭,则可能与操作系统线程设置有关:您是否尝试更改进程CPU关联性和优先级?

这应该非常特定于您正在运行的进程。没有任何实际的源代码来重现它,几乎不可能知道你发生了什么。有多少线程?什么是硬件配置(那里没有任何英特尔CPU提升 - 你使用的是笔记本电脑,你的能量设置是多少)?是使用CPU / FPU / MMX / SSE2(例如MMX和FPU不混用)?它会移动大量数据,还是处理一些现有数据?您的SW是否依赖于外部库(即使某些Windows库可能需要一些时间来初始化)?你如何使用内存(你试图预先分配内存;或者在多线程应用程序上,你尝试使用scaling MM而不是FastMM4)吗?

我认为使用示例分析器可能没有多大帮助,因为它会改变CPU核心的一般用途,但在所有情况下都值得尝试。我最好依赖于记录分析 - 参见例如this class或者您可以编写自己的时间戳,以查找应用中时间变化的位置。

AFAIK一直写到,在进行基准测试时,永远不会考虑第一次运行应用程序。现在计算机系统非常复杂,第一次,所有内部(SW和HW)管道都要被清除 - 所以当你从1个月的旅行回来后,你不应该喝掉自来水中的第一个水。 ;)

答案 3 :(得分:4)

我猜这是你的所有库/ DLL。这些通常在运行时按需加载,因此程序第一次运行操作系统时必须从磁盘读取它们。但是,一旦读取,它们将保持加载,除非您的系统开始内存不足。因此,如果您连续多次运行相同的程序,第一次运行将首当其冲,而另一次运行则受益于预加载的库。

答案 4 :(得分:2)

我能想到的其他因素是内存对齐(以及随后的缓存行填充),但是有两种类型:完美对齐(最快)和不完美(慢),人们会期望它不规则地发生(取决于记忆的布局方式)。

也许它与物理页面布局有关?据我所知,每个内存访问都通过MMU页表条目,因此分散的物理页面可能比连续页面慢。 (只是猜测,这个)

我还没有提到的另一件事是,您的进程正在运行哪个核心 - 特别是在超线程CPU上运行,在两个核心中较慢的运行可能会产生负面影响。尝试在每次运行时在同一个核心上设置处理器关联掩码,并查看这是否会影响第一次和后续运行之间测量的运行时差异。

顺便说一句 - 你如何定义'第一次运行'?难道你刚刚编译了可执行文件吗?在那种情况下(我只是在这里再次猜测),某些进程(操作系统,病毒扫描程序,甚至某些root工具包)可能正在忙于分析可执行文件的行为,可执行文件的行为可能会被跳过之前分析过。您可以尝试通过在运行之间更改可执行文件的一些随机不重要字节来证明,并查看是否会再次对运行时产生负面影响?

一旦找出原因,请发布摘要 - 这也可能有助于其他人。干杯!

答案 5 :(得分:2)

随便猜一下......

您的处理器是否支持自适应频率?也许只是处理器没有时间在第一次运行时调整其频率,并且在第二次运行时全速运行。

答案 6 :(得分:1)

有很多事情可以导致这种情况。就像一个例子:如果您使用ADO.NET进行数据访问并启用连接池(这是默认设置),则第一次运行应用程序时,将会创建数据库连接。当您的应用程序关闭时,连接将由ADO.NET维持在打开状态,因此下次您的应用程序运行并进行数据访问时,不必采取实例化连接的命中,因此会更快地显示

答案 7 :(得分:1)

猜猜你使用.net,如果我错了,你可以忽略我的大部分想法......

连接池,JIT编译,反射,IO缓存列表继续......

尝试测试代码的较小部分,以查看哪些部分最能改变性能......

您可以尝试使用程序集,因为这会删除JIT编译。

答案 8 :(得分:0)

  

那种减速来自何处以及我能做些什么呢。

我会谈到下一次从性能缓存中快速执行

  • 磁盘内部缓存(8MB或更多)
  • Windows applicationDependencies(作为DLL)/核心缓存
  • CPU缓存L3(如果某些编程循环足够小,则为L2)

所以你看到第一次没有从这些缓存系统中受益。