为啥Matlab在脚本“预热”后运行得更快?
Posted
技术标签:
【中文标题】为啥Matlab在脚本“预热”后运行得更快?【英文标题】:Why does Matlab run faster after a script is "warmed up"?为什么Matlab在脚本“预热”后运行得更快? 【发布时间】:2013-06-01 14:30:55 【问题描述】:我注意到我第一次运行脚本时,它比第二次和第三次花费的时间要多得多1。提到“热身”in this question 没有解释。
为什么“热身”后代码运行得更快?
我在两次调用之间不clear all
2,但是输入参数会随着每个函数调用而改变。有人知道为什么是这样吗?
1. 我的驾照在本地,所以不是驾照检查的问题。
2.实际上,如果我clear all
,行为不会改变。
【问题讨论】:
【参考方案1】:它在第一次之后运行得更快的一个原因是很多东西被初始化一次,它们的结果被缓存并在下一次被重用。例如在 M 端,变量可以定义为 persistent 在函数中可以是 locked。这也可能发生在MEX-side 的事物上。
此外,许多依赖项在第一次加载后被加载并保留在内存中以供重复使用。这包括 M 函数、OOP 类、Java 类、MEX 函数等。这适用于内置和用户定义的。
例如,在第一次运行脚本之前和之后发出以下command,然后进行比较:
[M,X,C] = inmem('-completenames')
注意clear all
不一定是clear以上所有,更不用说锁定功能了...
最后让我们不要忘记加速器的作用。不是每次调用函数时都解释 M 代码,而是在运行时将其编译为机器代码指令。 JIT 编译仅发生在第一次调用时,因此理想情况下,后续运行目标代码的效率将克服每次运行时重新解释程序的开销。
【讨论】:
感谢 Amro!如果只有必要的程序位于 P 代码缓冲区中,而不是我不感兴趣的其他函数调用中使用的所有内容,您是否知道脚本是否会运行得更快? IE。在对特定脚本进行基准测试之前,我应该clear all
吗?我没有注意到明显的不同,但我也没有“填满”缓冲区......
Amro,除非您的代码非常小,否则 JIT 编译是在第一次运行之后加速代码运行的主要效果 - 加载函数并不那么重要。此外,JIT 编译不仅仅发生在第一次调用时 - MATLAB 有时只会在第一次运行时 JIT 编译您的代码的一部分,然后再编译其他部分。 @RobertP.,如果您需要对代码进行基准测试,那么在您引用的问题中链接到的 timeit
函数是最好的方法。除其他外,它还处理您为您描述的所有“热身”问题。
@SamRoberts:我同意,这当然是 JIT 编译的承诺。如果一段代码被重复调用,我们希望 JIT 编译器在运行时检测到这个“热点”,并将其优化为直接机器代码。希望即使不是所有人都在同一时间运行。至于函数加载,我认为对于加载外部依赖项的开销更高的 MEX 函数来说,这是一个更大的问题。
@RobertP.:我怀疑清除内存会让事情运行得更快。即使这样做,你也不应该依赖语言实现的细节来优化你的程序性能。总是写惯用的代码【参考方案2】:
Matlab 被解释。如果您不预热代码,您将因解释而不是实际算法而浪费大量时间。这可能会显着扭曲计时结果。
至少运行一次代码将使 Matlab 能够实际编译适当的代码段。
【讨论】:
【参考方案3】:除了诸如 JIT 编译等特定于 Matlab 的原因之外,现代 CPU 还具有大型缓存、分支预测器等。即使在汇编语言中,预热这些也是基准测试的一个问题。
此外,更重要的是,现代 CPU 通常在低时钟速度下处于空闲状态,并且只有在持续负载几毫秒后才会跳转到全速。
英特尔的Turbo 功能变得更加时髦:当功率和热限制允许时,CPU 的运行速度可以超过其可持续的最大频率。因此,如果您不小心控制这些因素,您的基准测试的前 20 秒到 1 分钟可能会比其余部分运行得更快。
【讨论】:
【参考方案4】:Amro 和 Marc 未提及的另一个问题是内存(预)分配。 如果您的脚本没有预先分配它的内存,那么由于内存分配,它的第一次运行会非常慢。一旦它完成了第一次迭代,所有的内存都会被分配,因此脚本的连续调用会更有效率。
一个说明性的例子
for ii = 1:1000
vec(ii) = ii; %// vec grows inside loop the first time this code is executed only
end
【讨论】:
@Shai 关于预分配是正确的,但我不同意后果。执行此类操作的代码写得不好。预热这种代码会使其下次更快,但在这种情况下算法很糟糕。这就是为什么我觉得,在这种情况下,热身实际上会利用糟糕的代码来扭曲时间并掩盖糟糕的实现! @Shai 预分配是一件好事,但它对第一次和随后的调用具有相同的效果。在您的示例中,vec
不仅在第一次执行代码时增长,而且在每次执行代码时都会增长。
@Shai,抱歉,这并不完全正确,因为这是在脚本中,所以如果您再次运行脚本,vec
将已经存在。如果它在一个函数中,它会在每次执行代码时增长,而不仅仅是第一次。
@SamRoberts - 这真的取决于你如何初始化vec
。如果您使用循环在脚本中初始化 vec
,它将在脚本第一次运行时增长,并且在第二次(以及所有连续)运行时不会改变大小。以上是关于为啥Matlab在脚本“预热”后运行得更快?的主要内容,如果未能解决你的问题,请参考以下文章
为啥从标准模块(而不是用户窗体)调用 VBA 代码时运行得更快?
为啥 Java switch on contiguous ints 似乎在添加案例时运行得更快?
QT4:为啥 bash shell 比使用 QProcess 调用的相同脚本更快?