MINC/Tools/emma/emma-speed
我们经常听到刚接触 MATLAB 的人抱怨它很慢。MATLAB 本身确实存在速度权衡。由于它是一种解释型语言,而不是编译型语言(如 C 或 FORTRAN),因此它的速度几乎总是落后于用 C 等语言编写的自定义程序。但是,这种速度损失可以通过代码开发的便捷性来弥补。
幸运的是,通过仔细构建脚本可以显著提高 MATLAB 的速度。编写 MATLAB 脚本以提高速度时,需要遵循一条基本规则
- 在任何情况下,都应避免使用for循环在 MATLAB 中。
这条规则有一个简单的记忆推论
- 矢量化,矢量化,矢量化。
换句话说,只要有可能,就使用矢量运算。对于许多操作,通过对矢量化进行一些思考,代码速度可以提高几个数量级。不幸的是,对于 MATLAB 新手来说,矢量化可能难以直观地理解。由于大多数人在熟悉传统编程语言后才接触 MATLAB,因此他们倾向于使用for自动循环。
让我们考虑一个简单的例子。我们想要找到图像中值大于 2000 的点有多少个。在传统的编程语言中,我们可能会采用以下方法
j=0; for i=1:16384; if (PET(i)>2000) j=j 1; end end
这在 MATLAB 中执行大约需要一秒钟。矢量化的方法是使用 MATLABfind函数
length(find(PET>2000));
这大约需要 0.07 秒才能执行,因此比传统方法快大约 15 倍。现在,考虑以下相关问题。我们希望根据我们刚刚操作的图像创建掩码,其中任何值大于 2000 的点都被设置为 1,所有其他点都被设置为 0。对这个问题的传统解决方案可能类似于
mask=zeros(16384,1); for i=1:16384; if (PET(i)>2000) mask(i)=1; end; end;
这大约需要 0.81 秒才能执行。一种更快、矢量化的方法如下
mask2=zeros(16384,1); index=find(PET>2000); mask2(index)=ones(size(index));
这大约需要 0.05 秒才能执行,因此比传统方法快大约 16 倍。它还引入了一种在矢量化例程时非常有用的重要技术:使用矢量作为另一个矢量的索引。在上面的代码片段中,变量index包含值的索引。
在尝试加速 MATLAB 代码时,另一件需要记住的事情是,可以在 MATLAB 中调用 FORTRAN 或 C 例程。这些函数被称为 FMEX 或 CMEX 函数,具体取决于它们是用哪种语言编写的,有关创建它们的详细信息可以在 MATLAB 外部接口指南中找到。
EMMA 工具箱包含几个流行 MATLAB 函数的 CMEX 版本
- ntrapz是 MATLAB 的替代品trapz梯形积分函数。
- nfmins是 MATLAB 的替代品fmins函数最小化例程。
lookup
执行 1-D 线性插值,类似于 MATLAB 的interp1
例程。rescale
用于将大型矩阵按常数或按相同大小和形状的另一个矩阵进行缩放。rescale
与.*
运算符相比没有明显的加速优势,但它在原地执行算术运算,因此不会创建目标矩阵的副本。这可能会导致显著的内存节省。
这些例程在功能上与 MATLAB 例程相同,只是提供了速度提升。