我有很多很好的 MATLAB 代码,它们运行得太慢,用 C 重写会很痛苦。C 的 MATLAB 编译器似乎没有多大帮助,如果有的话。是否应该加快执行速度?我搞砸了吗?
11 回答
如果您使用的是MATLAB 编译器(在最新版本的 MATLAB 上),那么您几乎肯定不会看到任何加速。这是因为编译器实际上所做的只是为您提供了一种打包代码的方法,以便可以将其分发给没有 MATLAB 的人。它不会将其转换为更快的东西(例如机器代码或 C) - 它只是将其包装在 C 中,以便您可以调用它。
它通过让您的代码在本质上是 MATLAB 计算内核的 MATLAB Compiler Runtime (MCR) 上运行来实现这一点 - 您的代码仍在被解释。由于必须调用 MCR 所带来的损失,您可能会发现编译后的代码运行速度比在 MATLAB 上运行要慢。
换句话说——你可能会说编译器实际上并没有编译——至少在这个词的传统意义上。
旧版本的编译器工作方式不同,在某些情况下可能会出现加速。对于 Mathwork 的看法,请访问
http://www.mathworks.com/support/solutions/data/1-1ARNS.html
以我的经验,缓慢的 MATLAB 代码通常来自未对代码进行矢量化(即,编写 for 循环而不是仅仅将数组相乘(简单示例))。
如果您正在执行文件 I/O,请注意一次读取一个数据。在帮助文件中查找 fscanf 的矢量化版本。
不要忘记 MATLAB 也包含一个分析器!
我将回应 dwj 所说的话:如果您的 MATLAB 代码很慢,这可能是因为它没有充分矢量化。如果您在可以对整个数组执行操作时执行显式循环,那就是罪魁祸首。
这同样适用于所有面向数组的动态语言:Perl 数据语言、Numeric Python、MATLAB/Octave 等。在编译的 C 和 FORTRAN 编译代码中甚至在某种程度上也是如此:专门设计的向量化库通常使用精心手工编码的内部循环和 SIMD 指令(例如 MMX、SSE、AltiVec)。
首先,我支持上述所有关于分析和矢量化的评论。
从历史的角度来看...
旧版本的 Matlab 允许用户通过预先解析 m 代码并将其转换为一组 matlab 库调用来将 m 文件转换为 mex 函数。这些调用具有解释器所做的所有错误检查,但旧版本的解释器和/或在线解析器速度很慢,因此编译 m 文件有时会有所帮助。通常当你有循环时它会有所帮助,因为 Matlab 足够聪明,可以在 C 中内联其中的一些。如果你有这些版本的 Matlab 之一,你可以尝试告诉 mex 脚本保存 .c 文件,你可以确切地看到它是什么正在做。
在更新的版本中(可能是 2006a 及更高版本,但我不记得了),Mathworks 开始使用即时编译器作为解释器。实际上,这个 JIT 编译器会自动编译所有的 mex 函数,所以显式地离线执行它根本没有帮助。从那时起,在每个版本中,他们也付出了很多努力来提高解释器的速度。我相信较新版本的 Matlab 甚至不允许您将 m 文件自动编译为 mex 文件,因为它不再有意义。
MATLAB 编译器封装您的 m 代码并将其分派到 MATLAB 运行时。因此,您在 MATLAB 中看到的性能应该是您在编译器中看到的性能。
根据其他答案,对代码进行矢量化很有帮助。但是,如今 MATLAB JIT 已经相当不错了,而且很多东西在矢量化方面的表现都大致相同。这并不是说矢量化没有性能优势,只是它不再是曾经的灵丹妙药。真正分辨的唯一方法是使用分析器找出您的代码在哪里遇到瓶颈。很多时候,您可以在某些地方进行本地重构以真正提高代码的性能。
您可以采用其他几种硬件方法来提高性能。首先,大部分线性代数子系统都是多线程的。如果您在多核或多处理器平台上工作,您可能需要确保已在首选项中启用该功能。其次,您可以使用并行计算工具箱来更多地利用多个处理器。最后,如果您是 Simulink 用户,您或许可以使用 emlmex 将 m-code 编译成 c。这对于定点工作特别有效。
您是否尝试过分析您的代码?你不需要向量化你的所有代码,只需要控制运行时间的函数。MATLAB 分析器将为您提供一些关于您的代码花费最多时间的提示。
您应该阅读MathWorks 手册中提高性能的技巧部分的许多其他内容。
mcc 根本不会加速你的代码——它不是真正的编译器。
在你放弃之前,你需要运行分析器并弄清楚你所有的时间都花在了哪里(工具->打开分析器)。此外,明智地使用“tic”和“toc”会有所帮助。在你知道时间在哪里之前不要优化你的代码(不要试图猜测)。
请记住,在 matlab 中:
- 位级操作真的很慢
- 文件 I/O 很慢
- 循环通常很慢,但向量化很快(如果你不知道向量语法,学习它)
- 核心操作非常快(例如矩阵乘法,fft)
- 如果你认为你可以在 C/Fortran/etc 中做一些更快的事情,你可以写一个 MEX 文件
- 有将 matlab 转换为 C 的商业解决方案(谷歌“matlab to c”)并且它们有效
您可以将代码移植到“嵌入式 Matlab”,然后使用 Realtime-Workshop 将其翻译成 C。
嵌入式 Matlab 是 Matlab 的一个子集。它不支持元胞数组、图形、动态大小的 Marice 或某些矩阵寻址模式。移植到嵌入式 Matlab 可能需要付出相当大的努力。
Realtime-Workshop 是代码生成产品的核心。它提供通用 C,或者可以针对一系列嵌入式平台进行优化。您最感兴趣的可能是 xPC-Target,它将通用硬件视为嵌入式目标。
我会投票支持分析+然后看看瓶颈是什么。
如果瓶颈是矩阵数学,你可能不会做得更好......除了一个大问题是数组分配。例如,如果你有一个循环:
s = [];
for i = 1:50000
s(i) = 3;
end
这必须不断调整数组的大小;预先调整数组的大小(从零或 NaN 开始)并从那里填充它要快得多:
s = zeros(50000,1);
for i = 1:50000
s(i) = 3;
end
如果瓶颈是重复执行大量函数调用,那就很难了。
如果瓶颈是 MATLAB 不能快速完成的事情(某些类型的解析、XML 等),那么我会使用 Java,因为 MATLAB 已经在 JVM 上运行并且它非常容易与任意 JAR 文件接口。我查看了与 C/C++ 的接口,它真的很难看。Microsoft COM 还可以(仅在 Windows 上),但在学习了 Java 之后,我认为我不会再回到那个状态了。
正如其他人所指出的,缓慢的 Matlab 代码通常是向量化不足的结果。
然而,有时即使是完美的矢量化代码也很慢。然后,您还有更多选择:
- 看看是否有任何你可以使用的库/工具箱。这些通常写得非常优化。
- 分析您的代码,找到漏洞并用纯 C 重写那些。将 C 代码(例如 DLL)连接到 Matlab 很容易,并且在文档中进行了介绍。
通过 Matlab 编译器,您可能指的是命令 mcc,它通过绕过 Matlab 解释器来稍微加快代码速度。显着加速 MAtlab 代码(50-200 倍)的是使用由 mex 命令编译的实际 C 代码。