当我testing编写代码时,我注意到的一点是,长时间运行的程序在运行程序的第一次运行时往往比在后续运行时运行时间要长,有时甚至超过10倍。 很明显,这里有一些冷caching/热caching的问题,但我似乎无法弄清楚它是什么。
这不是CPUcaching,因为这些长时间运行的操作往往是循环,我提供了大量的数据,并且应该在第一次迭代之后完全加载。 (另外,卸载和重新加载程序应该清除caching。)
另外,它不是光盘caching。 我已经通过从光盘上加载所有数据并在之后处理来排除这种情况,这是实际的CPU数据处理过程缓慢的原因。
那么什么事情会导致我的程序在第一次运行时运行缓慢,但是如果closures它并再次运行它,运行速度会更快吗? 我已经在几个不同的程序中看到了这一点,它们做了很多不同的事情,所以这似乎是一个普遍的问题。
编辑:澄清,我在Delphi写,但我真的不认为这是一个Delphi特定的问题。 但是这意味着无论问题是什么,它都与JIT问题,垃圾收集问题或托pipe代码带来的任何其他包袱无关。 而我没有处理networking连接。 这是纯粹的CPU绑定处理。
一个例子:一个脚本编译器。 它像这样运行:
如果我把大量的脚本文件(~100K行)从光盘载入到内存中,lex步骤第一次运行需要大约15秒,而后续运行需要2秒。 (是的,我知道还有很长一段时间,我正在研究这个…)我想知道经济放缓是从哪里来的,我能做些什么。
三件事情要尝试:
即使(特别是)对于非常小的命令行程序,问题可能是加载进程所花费的时间,链接到动态链接的库等。我相信现代操作系统避免重复大量的这项工作,如果相同的程序是一次或两次运行。
我也不会轻易放弃CPU缓存。 0级高速缓存与内部循环非常相关,但是对于同一个应用程序的第二次运行则更少。 在我便宜的Athlon 2 X4 645系统上,每个核心有64K + 64K(数据+指令)0级缓存 – 不是很大的内存。 一级缓存是每个内核IIRC 512K,因此不太可能被启动新的程序运行所需的O / S代码,调用操作系统服务和标准库等所污染。二级缓存(在拥有它的CPU上 – 我的Athlon 2不是,IIRC)还是比较大的,而且主板/芯片组可能会有更高的级别和更大的缓存。
还有至少一种其他类型的缓存 – 分支预测表。 虽然我以为他们会比0级缓存更加糟糕,甚至更快。
我通常发现单元测试程序第一次运行速度要慢很多倍。 但是,程序越大越复杂,效果越不明显。
一段时间以来,应用程序的性能往往被认为是不确定的。 虽然这不是严格的,但表现是由很多难以预测的因素决定的,这是一个很好的模型。 例如,如果CPU有点暖,则可以降低时钟速度以防止过热。 芯片不同部分的温度也不尽相同,芯片上的变化以复杂的方式传导。 由于时钟速度的变化和不同代码片段的不同需求改变了温度变化的模式,混沌(如在混沌理论中)的行为有明显的可能性。
在某些平台上,如果程序的第一次运行是在“快速”(而不是冷却/静音)模式下运行处理器,那么我不会感到惊讶,这意味着第二次运行的开始将从该速度中受益提升以及结束。 然而,这将是一个棘手的 – 它将不得不是一个CPU密集型的程序,如果你的冷却不足,处理器可能会再次减速,以避免过热。
我通常经历了相反的情况:对于计算强度大的工作(如果反病毒工作不正常),在调用之间只有5-10%的差异。 例如,为我们的框架运行的6,000,000次回归测试的运行时间非常稳定,而且是非常耗费磁盘和CPU的工作。
我真的不相信CPU缓存或流水线/分支预测问题,因为处理的数据和代码似乎是一致的,正如你写的。 如果防病毒功能关闭,则可能与操作系统线程设置有关:是否尝试更改进程CPU亲和性和优先级?
这应该是非常具体的你正在运行的过程。 没有任何实际的源代码来重现它,几乎不可能知道你正在发生什么。 有多少个线程? 什么是硬件配置(在那里没有任何英特尔CPU提升 – 你使用笔记本电脑,你的能量设置是什么)? 是否使用CPU / FPU / MMX / SSE2(例如MMX和FPU不混合)? 它移动了大量的数据,还是处理了一些现有的数据? 你的SW依赖于外部库(甚至一些Windows库可能需要一些时间来初始化)? 你如何使用内存(你是否尝试预先分配内存;或者在多线程应用程序中,是否尝试使用缩放MM而不是FastMM4)?
我认为使用示例分析器可能不会有太大的帮助,因为它会改变一般的CPU核心使用,但值得在所有情况下尝试。 我最好依靠日志分析 – 例如,参见这个课程,或者您可以编写自己的时间戳,以查找您的应用程序的时间更改的位置。
AFAIK总是写道,在进行基准测试时,永远不会考虑第一次运行应用程序。 现在的计算机系统非常复杂,第一次,所有的内部(软管和硬件)管道都要清洗 – 所以当你从旅行的一个月回来时,你不应该喝水龙头里的第一个水。 ;)
我猜这是所有的库/ DLL。 这些通常是在运行时按需加载的,所以当你的程序第一次运行时,操作系统将不得不从磁盘读取所有的内容。 一旦阅读,但他们将保持加载,除非你的系统开始内存不足。 因此,如果连续多次运行相同的程序,第一次运行首当其冲,而另一次则从预加载的库中受益。
我能想到的其他因素是内存对齐(以及随后的缓存行填充),但是有两种类型:完美对齐(最快)和不完美(较慢),可以预料它会不规则地发生内存是如何布置的)。
也许这与物理页面布局有关? 据我所知,每个内存访问通过MMU页表项,所以分散的物理页面可能比连续的页面慢。 (只是一个疯狂的猜测,这一个)
我还没有提到的另一件事是,你的进程运行在哪个核心上 – 尤其是在超线程的CPU上,在两个较慢的核心上运行可能会产生负面影响。 尝试在每个运行的同一个内核上设置处理器关联掩码,并查看是否会影响第一次和后续运行之间测得的运行时间差异。
顺便说一句 – 你如何定义“第一次运行”? 难道是你刚才编译的可执行文件? 在这种情况下(我只是在这里再次猜测),一些进程(操作系统,病毒扫描程序甚至一些rootkit)可能正在忙于分析可执行文件的行为,一旦可执行文件之前分析过。 你可以试着证明,通过在运行之间更改一些随机的不重要的可执行字节,看看是否会对运行时间产生负面影响?
一旦找出原因,请发表一个总结 – 这也可以帮助其他人。 干杯!
只是一个随机猜测…
你的处理器支持自适应频率吗? 也许只是处理器在第一次运行时没有时间去适应它的频率,而在第二次运行时却是全速运行。
有很多事情可以导致这一点。 举一个例子:如果您使用ADO.NET
进行连接池打开的数据访问(这是默认设置),那么当您的应用程序第一次运行时,将会创建数据库连接。 当您的应用程序关闭时, ADO.NET
将连接保持在打开状态,所以下次您的应用程序运行并进行数据访问时,就不必实例化连接,从而显得更快。
猜你使用.NET如果我错了,你可以忽略我的想法大部分…
连接池,JIT编译,反射,IO缓存列表继续和….
尝试测试代码的较小部分,以查看哪些部分最能改变性能
您可以尝试使用您的程序集,因为这会删除JIT编译。
那里放缓是从哪里来的,我能做些什么呢。
我会谈论下一次可以从性能缓存快速执行
所以你第一次看到这些缓存系统没有好处。