MATLAB函数第一次很慢,但后来快得多。为什么?

MATLAB函数第一次很慢,但后来快得多。为什么?,matlab,benchmarking,Matlab,Benchmarking,我有一个很大的MATLAB函数文件。它首先创建一个零矩阵,然后通过计算函数中硬编码的大量对应(长)代数表达式来更新大约70%的单元。完成后,将返回一个数值矩阵 .m文件大约4MB大(我有100个这样的m.m文件,但这并不直接相关)。当我第一次评估函数时,评估大约需要9秒。然而,后续的运行只需要大约0.1秒,这比我预期的要多 为什么第一次评估需要9秒?每当我关闭并重新打开MATLAB时,我每次都会进行缓慢的第一次评估,后续的运行速度要快得多。为什么会这样 m。文件可在以下公共链接中找到(您可以从浏

我有一个很大的MATLAB函数文件。它首先创建一个零矩阵,然后通过计算函数中硬编码的大量对应(长)代数表达式来更新大约70%的单元。完成后,将返回一个数值矩阵

.m文件大约4MB大(我有100个这样的m.m文件,但这并不直接相关)。当我第一次评估函数时,评估大约需要9秒。然而,后续的运行只需要大约0.1秒,这比我预期的要多

为什么第一次评估需要9秒?每当我关闭并重新打开MATLAB时,我每次都会进行缓慢的第一次评估,后续的运行速度要快得多。为什么会这样

m。文件可在以下公共链接中找到(您可以从浏览器复制文本):

应使用的命令窗口输入为:
[测试]=K_a_12_102x(414000000,1.1095e+09,1.2500e-04,0.0840,0.0240,0.0240,0.0020,0.0020,0,0,0,0,0,0,3.0397e+08,8.9930e+07,0,3.0397e+08,0,0,1.0702e+08,0,0,0,0,0,0,0,0,0,0,0497.7389,80.7355,-15.9811391.1985,-9811103.524840000,0.040.040)我认为这是JIT的汇编。第一次执行文件时,MATLAB必须对其进行解释(将文本转换为机器代码)

后续运行使用缓存的机器代码,执行速度更快。为了验证这一点:在代码中进行了一个小的更改之后,MATLAB将需要重新编译—因此下次再次运行时应该会比较慢。(我这样做的结果正好如此。)您正在执行大量的简单操作,这些操作应该执行得非常快。机器代码的转换正在减慢速度

为了加速整个过程:将代码传输到C、C#或类似的东西,并将其作为DLL文件包含。您将拥有恒定且快速的计算,但您无法轻松地更改它们

(使用C#DLL文件,您还可以进行一些JIT编译,但它的频率较低,而且可能比MATLAB更快。)


我已经做了一些编码,并将代码移植到C#。初始计时为13.4s和0.15s(第一次/第二次运行)

简单端口,发布配置:

>> test_1
Elapsed time is 124.7 seconds.
>> test_1
Elapsed time is 0.0297 seconds.

因此,第一次运行要比MATLAB-bummer糟糕得多。虽然我喜欢C#,但它可能不是这份工作的最佳工具。(顺便说一句:我不得不使用命令行编译器,因为VisualStudio2010不断崩溃…

此外,除了首席执行官提到的JIT编译器的效果之外,可能还有各种影响。要么Matlab本身足够聪明,可以重用它的一些数据结构,要么它的一些代码已经存储在处理器的高速缓存中,而不是主内存中。事实上,即使JIT本身也依赖于缓存编译结果,否则每次调用函数时都必须重新编译。此外,所有现代操作系统都进行各种缓存,因此,您不必从磁盘读取某些数据文件、MEX文件或DLL,而只需从内存中获取

这就是为了准确测量某些函数的执行速度,不应该使用simple
tic();toc()
语句,但要使用这样的函数(使用它,它很好!)。这会重复多次测量以“预热”缓存,并始终丢弃前几次测量

至于为什么matlab对于这个特定的文件速度慢,我完全可以理解。我的文本编辑器需要一分钟多的时间才能打开它,您有大约100行,其中包含非常长的表单语句

K_a_12=zeros(1089,100);
K_a_12(1011,1) = 2*h_a*((x*(250*G_a*L2^20*W + 5250*G_a*L1^2*L2^18*W + ...
K_a_12(1011,3) = 2*h_a*((x^13*(188955000*G_a*L2^8*W*h_1 +  ...

我希望是自动生成的。在我看来,这个计算可以更有效率地进行。首先,您似乎只填充了矩阵的一小部分,所以您可能应该使用一个。接下来,通过快速检查,每个术语的形式似乎都是
h_a*x^n1*const*L1^n2*L2^n3*…
,可以简化计算。我敢打赌,整个计算可以在几行内完成,只需对几个矩阵进行乘法和幂运算,这些矩阵应保存为mat文件,而不是像完整写出的计算那样。最后,您还没有使用函数的大约一半输入参数。

在JIT编译器引入MATLAB之前很久,第一次运行速度就一直很慢,甚至对于MEX文件来说也是如此,JIT编译器没有应用在MEX文件上。当您第一次运行代码时,MATLAB必须从磁盘加载代码,解析代码(请参阅下面的运行时类型分析详细信息),如果它是.m文件,则应用JIT编译。然后在执行时,为数据分配空间,并将指令加载到CPU缓存中,在那里它们可能停留在非常快的访问时间,以便进一步执行。据我所知,这就是为什么在MATLAB世界之外到处都有“缓存升温”程序的原因(对硬件爱好者挥手致歉)。然而,对于.m文件,磁盘访问可能是一个很大的因素,即使对于比您的情况中的“大约4MB大”小得多的文件也是如此。还有一个额外的步骤

要在MEX文件中看到这种情况,只需运行
清除MEX
并计时函数调用。MATLAB必须将其从磁盘重新加载到内存中,很可能是使用无效的CPU缓存

运行时类型分析 代码加速特性的第二个方面(JIT代码生成是第一个方面)是运行时类型分析。来自旧的MathWork白皮书:

运行时类型分析基于以下前提:如果以前处理过一行M代码,则变量很可能具有与系统上次看到这行代码时相同的类型和形状第一次执行一行代码时,系统检查变量,并为找到的数据类型和形状生成特定代码。该行的后续执行可以重新执行
>> feature accel on
>> clear K_a_12_102x
>> x = rand(1000); tic,for t=1:1e6, x=x; end,toc % control
Elapsed time is 0.083878 seconds.
% do first-run of K_a_12_102x, took 13.280327 sec
>> tic; [test]=K_a_12_102x(414000000,1.1095e+09,1.2500e-04,0.0840,0.0840,0.0240,0.0240,0.0020,0.0020,0,0,0,0,3.0397e+08,8.9930e+07,0,3.0397e+08,0,1.0702e+08,0,0,0,0,0,0,497.7389,80.7355,-15.9811,391.1985,-15.9811,103.5248,20440000,0,20440000,0.06); toc
Elapsed time is 0.151804 seconds.
>> feature accel off
>> clear K_a_12_102x
>> tic,for t=1:1e6, x=x; end,toc % control
Elapsed time is 0.630039 seconds.
% do a first-run of K_a_12_102x, took 15.634775 seconds
>> tic; [test]=K_a_12_102x(414000000,1.1095e+09,1.2500e-04,0.0840,0.0840,0.0240,0.0240,0.0020,0.0020,0,0,0,0,3.0397e+08,8.9930e+07,0,3.0397e+08,0,1.0702e+08,0,0,0,0,0,0,497.7389,80.7355,-15.9811,391.1985,-15.9811,103.5248,20440000,0,20440000,0.06); toc
Elapsed time is 0.159683 seconds.