早在将 JIT 编译器引入 MATLAB 之前,首次运行速度就很慢,即使是 MEX 文件也是如此,在其上未应用 JIT 编译器。当您第一次运行代码时,MATLAB 必须从磁盘加载它,解析代码(请参阅下面的运行时类型分析详细信息),如果它是 .m 文件,则应用 JIT 编译。然后在执行时,为数据分配空间,并将指令加载到 CPU 缓存中,在那里它们可能会保持非常快的访问时间以供进一步执行。据我了解,这就是在 MATLAB 世界之外无处不在的“缓存预热”程序的原因(对我的挥手致意的硬件爱好者表示歉意)。但是,对于 .m 文件,磁盘访问可能是一个重要因素,即使文件比您的情况“大约 4MB 大”小得多。当多个函数具有相同的名称时,还增加了一个函数消歧步骤。
要查看 MEX 文件发生的这种情况,只需运行clear mex
并计时函数调用。MATLAB 必须重新将其从磁盘加载到内存中,并且可能使用无效的 CPU 缓存。
运行时类型分析
代码加速特性的第二个方面(JIT 代码生成是第一个),是运行时类型分析。来自旧的 MathWork 白皮书:
运行时类型分析基于以下前提:如果之前已经处理过一行 M 代码,则很可能变量的类型和形状与系统上次看到该行时相同。第一次执行一行代码时,系统会检查变量并为找到的数据类型和形状生成特定代码。只要系统验证变量类型和大小没有改变,该行的后续执行就可以重用此代码。由于类型很少更改,因此后续执行会尽可能快地运行。如果类型确实发生了变化,则重新生成代码。
您可能会考虑 JIT 编译过程的这一部分,确实如此。但关键是,无论加速器是否决定对任何代码行进行 JIT 编译,这种分析都是在第一次执行时运行的。顺便说一句,整个文件不会被编译成机器代码。过去可以看到在分析器中使用哪些行得到了加速setpref('profiler','showJitLines',1);
,但不幸的是,它作为一个特性被删除了。
无论如何,在实际查看您的代码之后,从磁盘加载文件后需要解析的常量和变量数量惊人。一行超过 31,000 个字符,包含数千个数字文字!分析和决定需要编译的内容以及在运行之间可以缓存的内容很多。好像为了证明这一点,只是查看(不运行)您的代码设法使编辑器DirectUI::DUIXmlParser::InitializeParserFromXmlLiteReader
在堆栈跟踪上崩溃。哎呀,那是一些讨厌的代码!
JIT 编译器是否为此函数生成代码?
让我们在打开 MATLAB 加速功能的情况下为代码计时。我们还进行了控制测试,我们知道在没有加速的情况下运行速度会慢 8 倍。
>> 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.
发现
研究结果有两个方面:
- 禁用加速 (JIT) 后首次运行时间未得到改善(JIT 开启时为 13.28 秒,而 JIT 关闭时为 15.63 秒)。
- 随后的运行表明,启用 JIT 时不会生成任何机器代码(0.1518 秒,JIT 开启与 0.1597 秒,JIT 关闭)
简而言之,您的代码不会从 JIT 加速中受益,并且 JIT 执行/分析不会增加首次运行的执行时间。
问题仍然存在,是什么导致首次运行时间缓慢?一些可能性:从磁盘加载代码文本,在将代码保存到 RAM 之前对其进行解析(去除注释和空格),不重用从先前运行中保存的变量初始化,可能是保存在 CPU 缓存中的函数使用的核心 MATLAB 指令,以及任何MATLAB 进行运行时语法检查所必需的非 JIT 相关代码分析。该文件是 4MB 并且就方程长度和数字文字的绝对数量而言非常复杂的事实表明它不是 CPU 缓存,而是初始文件加载和代码分析。