提高MATLAB运行效率.docx
《提高MATLAB运行效率.docx》由会员分享,可在线阅读,更多相关《提高MATLAB运行效率.docx(27页珍藏版)》请在冰点文库上搜索。
提高MATLAB运行效率
用过Matlab的人都知道,Matlab是一种解释性语言,存在计算速度慢的问题,为了提高程序的运行效率,matlab提供了多种实用工具及编码技巧。
1.循环矢量化
Matlab是为矢量和矩阵操作而设计的,因此,可以通过矢量化方法加速M文件的运行。
矢量化是指将for循环和while循环转换为等价的矢量或矩阵操作。
下面给出一个循环的例子:
i=0;
forn=0:
0.1:
1000
i=i+1;
y(i)=cos(n);
end
那么我们可以矢量化为:
n=0:
0.1:
1000;
y=cos(n);
我们可以用tic和toc函数来查看上述各代码运行的时间,采用for循环的程序0.39秒(具体时间和计算机配置有关),而矢量化后几乎耗时为0。
2.给数组或矩阵预分配内存
特别是使用大型数组或矩阵时,Matlab进行动态内存分配和取消时,可能会产生内存碎片,这将导致大量闲置内存产生,预分配可通过提前给大型数据结构预约足够空间来避免这个问题。
3.用函数代替脚本文件
因为每次调用MATLAB的脚本文件都需要将不必要的中间变量加载到内存中,每执行一次,就加载一次。
函数在调用时被编译成了伪代码,只需要加载到内存一次。
当多次调用同一个函数时会运行快一些。
因此尽量多使用函数文件而少使用脚本文件,也是提高执行效率的一种方法。
4.用Mex文件编写循环代码
Matlab提供了与C和C++的接口,那么我们可以在用C或C++语言编写耗时的循环代码,然后通过接口程序在Matlab中转换成dll文件,这就是我们所要的Mex文件,通过这种方法可以极大地提高计算速率。
1. 尽量避免使用循环结构
MATLAB变量的基本类型是矩阵,当对矩阵的每个元素循环处理时,运算速度很慢。
因此编程时应尽量把数组和矩阵看作一个整体来进行编程,而不是像其他的程序设计语言那样,使用循环结构对矩阵的元素循环进行处理。
利用MATLAB提供的用于矢量化操作的函数,把循环矢量化,这样既可以提高编程效率,也可以提高程序的执行效率。
下面给出一个循环的例子:
i=0;
forn=0:
0.1:
100
i=i+1;
y(i)=cos(n)
end
上述程序段把数组中的每个元素都进行函数值计算,这样会耗费大量的运算时间,我们可以把数组看作一个整体来处理,计算函数值,可以修改这个程序段如下。
n=0:
0.1:
100;
y=cos(n)
通过使用MATLAB专门提供的测试程序运行时间的函数,可以发现,把数组看作一个整体,进行操作后,执行效率提高约300倍。
另外,在必须使用多重循环的情况下,建议在循环的外环执行循环次数少的,内环执行循环次数多的,这样也可以显著提高程序执行速度。
2. 在使用数组或矩阵之前先定义维数
MATLAB中的变量在使用之前不需要明确地定义和指定维数。
但当未预定义数组或矩阵的维数时,当需赋值的元素下标超出现有的维数时,MATLAB 就为该数组或矩阵扩维一次,这样就会大大降低程序的执行效率。
因此,在使用数组或矩阵之前,预定义维数可以提高程序的执行效率。
3. 对矩阵元素使用下标或者索引操作
在MATLAB中,矩阵元素的引用可用两个下标来表示。
例如:
A(i,j)表示矩阵的第i行第j列的元素;A(1:
k,j)表示矩阵A的第j列的前k个元素;A(:
j)表示矩阵的第j列的所有元素。
求矩阵A的第j列元素的平均值的表达式为mean(A(:
j))。
4. 尽量多使用函数文件少使用脚本文件
因为每次调用MATLAB的脚本文件都需要将不必要的中间变量加载到内存中,每执行一次,就加载一次。
函数在调用时被编译成了伪代码,只需要加载到内存一次。
当多次调用同一个函数时会运行快一些。
因此尽量多使用函数文件而少使用脚本文件,也是提高执行效率的一种方法。
5. 在必须使用循环时,可以考虑转换为C-MEX
当必须使用耗时的循环时,可以考虑将循环体中的语句转换为C-MEX。
C-MEX是将M文件通过MATLAB的编译器转换为可执行文件,是按照MEX技术要求的格式编写相应的程序,通过编译连接,生成扩展名为.dll的动态链接库文件,可以在MATLAB环境下直接执行。
这样,循环体中的语句在执行时不必每次都解释(interpret)。
一般来说,C-MEX文件的执行速度是相同功能的M文件执行速率的20~40倍。
编写C-MEX不同于M文件,需要了解MATLAB C-MEX规范。
幸运的是MATLAB提供了将M文件转换为C-MEX的工具。
6. 内存优化
MATLAB在进行复杂的运算时需要占用大量的内存。
合理使用内存和提高内存的使用效率,可以加快运行速度,减少系统资源的占用。
7. 内存管理函数和命令
(1)Clearvariablename:
从内存中删除名称为variablename的变量。
(2)Clearall:
从内存中删除所有的变量。
(3)Save:
将指令的变量存入磁盘。
(4)Load:
将save命令存入的变量载入内存。
(5)Quit:
退出MATLAB,并释放所有分配的内存。
(6)Pack:
把内存中的变量存入磁盘,再用内存中的连续空间载回这些变量。
考虑到执行效率问题,不能在循环中使用。
8. 节约内存的方法
(1)避免生成大的中间变量,并删除不再需要的临时变量。
(2)当使用大的矩阵变量时,预先指定维数并分配好内存,避免每次临时扩充维数。
(3)当程序需要生成大量变量数据时,可以考虑定期将变量写到磁盘,然后清除这些变量。
当需要这些变量时,再重新从磁盘加载。
(4)当矩阵中数据极少时,将全矩阵转换为稀疏矩阵。
提高MATLAB程序效率的几点原则,这些都是俺在这两年中参加四次数模编写大量m程序总结的经验,加之网上很多英雄也是所见略同。
1.“计算向量、矩阵化,尽量减少for循环。
”[/B]
因为MATLAB本来就是矩阵实验室的意思,他提供了极其强大而灵活的矩阵运算能力,你就没必要自己再用自己编写的for循环去实现矩阵运算的功能了。
另外由于matlab是一种解释性语言,所以最忌讳直接使用循环语句。
但在有些情况下,使用for循环可以提高程序的易读性,在效率提高不是很明显的情况下可以选择使用for循环。
口说无凭,下面是利用tic与toc命令计算运算所用时间的方法,测试两种编程的效率。
需要说明的是没有办法精确计算程序执行时间,matlab帮助这样写到“Keepinmindthatticandtocmeasureoverallelapsedtime.MakesurethatnootherapplicationsarerunninginthebackgroundonyoursystemthatcouldaffectthetimingofyourMATLABprograms.”意思是说在程序执行的背后很可能有其他程序在执行,这里涉及到程序进程的的问题,m程序执行的过程中很可能有其他进程中断m程序来利用cup,所以计算出来的时间就不仅是m程序的了,在这种情况下我把那些寄点去掉,进行多次计算求他的平均时间。
n=100;
A(1:
1000,1:
1000)=13;
C(1:
1000,1)=15;
D(1:
1000,1)=0;
fork=1:
n
D(:
)=0;
tic
fori=1:
1000
forj=1:
1000
D(i)=D(i)+A(i,j)*C(j);
end
end
t1(k)=toc;
%------------------
D(:
)=0;
tic
D=A*C;
t2(k)=toc;
end
u=t1./t2;
u(u<0)=[];
plot(u)
p=mean(u)
t1、t2分别代表用for循环编程和矩阵化编程计算矩阵乘向量所用时间,u代表时间的比值。
u(u<0)=[];是认为t1不可能小于t2,所以去掉不可能出现的情况。
然后画出图形计算平均值。
经多次试验结果大致相同,其中一次结果如下:
p=
9.6196
------------t1时间是t2的十倍左右。
2.“循环内大数组预先定义--预先分配空间”[/U]
这一点原则是极其重要的,以至于在编写m程序时编辑器会给出提示“'ver'mightbegrowinginsidealoop.Considerprealloactingforspeed.”
clear
n=50;
m=1000;
fork=1:
n
A=[];
tic
A(1:
m,1:
m)=3;
fori=1:
m
A(i,i)=i;
end
t1(k)=toc;
%------------
A=[];
tic
forj=1:
m
A(j,j)=j;
end
t2(k)=toc;
end
t2(t1>10^9)=[];
t1(t1>10^9)=[];
plot([t1;t2]')
t1、t2分别表示预先分配空间和循环中分配空间的时间,下图上面一条t2、下面t1
3.“尽可能利用matlab内部提供的函数”[/U]
因为matlab内部提供的函数绝对是各种问题的最优算法,那写程序都是他们大师级人物写出来的,程序应该说相当高效,有现成的为什么不用那!
这个原则就不用实际的程序测试了。
关于MATLAB程序提速的问题,可以参考网上很多智者的文章,都比较经典。
也可以看看我的上一篇文章,和网上大部分帖子有点不同,我是以实际的测试程序作为依据对如何提高MATLAB程序速度进行介绍的。
这里我再补充几点大家需要注意的。
下面是我在国内一个比较出名的论坛看到的关于m程序提速的帖子,开始还真以为他们谈论的都应该遵循。
(尽信书不如无书)
帖子的一部分这样说道:
“当要预分配一个非double型变量时使用repmat函数以加速,如将以下代码:
A=int8(zeros(100));
换成:
A=repmat(int8(0),100,100);”
凡事不能只凭自己的感觉,没有一点实际的例子,对于权威我们还要有挑战精神那,就不用说现在还不是经典的观点了。
下面是我写的测试程序,我本来是想得到这位网友大哥的结果,但是实事不是我们想象的那么简单。
n=100;
m=1000;
fork=1:
n
tic
A=int8(ones(m));
t1(k)=toc;
tic
B=repmat(int8
(1),m,m);
t2(k)=toc;
end
plot(1:
n,t1,'r',1:
n,t2)
isequal(A,B)
可以看出下面的红线是t1,而且最后的一句返回1,说明两种形式返回的值完全一样。
由此我想说的是,不管是在我们做论文,还是写博客的时候,别直接从网上或者别人文章那儿找点知识定理之类的补充自己那苍白无力的文章。
最好是自己动手编一下,“实践是检验真理的唯一标准”。
经过这一测试,我感觉有必要,也有责任对这个论坛上的一大批经典谈论加以测试。
尽管这个结论是错误的但这还不足以证明论坛上的帖子都不是经典。
还有一点关于m程序提速的这样说到:
“在必须使用多重循环时下,如果两个循环执行的次数不同,则在循环的外环执行循环次数少的,内环执行循环次数多的。
这样可以显著提高速度。
”
n=1000;
A=ones(1000)*13;
fork=1:
n
tic
fori=1:
10
forj=1:
1000
A(i,j)=A(i,j)*15;
end
end
t1(k)=toc;
tic
fori=1:
1000
forj=1:
10
A(i,j)=A(i,j)*16;
end
end
t2(k)=toc;
end
t2(t1>10^9)=[];
t1(t1>10^9)=[];
t1(t2>10^9)=[];
t2(t2>10^9)=[];%去除外界因素影响所产生的寄点
plot(1:
size(t1,2),t1,'r',1:
size(t1,2),t2)
由这个时间图可以看出for循环的嵌套顺序对于速度是有影响的,虽然相对来说差别不是很大,但是毕竟论坛上的观点是正确的。
至于他所说的“显著”二字就没必要加上了。
此论坛还有一些提速的观点列举如下:
“遵守PerformanceAcceleration的规则
关于什么是“PerformanceAcceleration”请参阅matlab的帮助文件。
我只简要的将
其规则总结如下7条:
1、只有使用以下数据类型,matlab才会对其加速:
logical,char,int8,uint8,int16,uint16,int32,uint32,double而语句中如果使用了非以上的数据类型则不会加速,如:
numeric,cell,structure,single,functionhandle,javaclasses,userclasses,int64,uint64
2、matlab不会对超过三维的数组进行加速。
3、当使用for循环时,只有遵守以下规则才会被加速:
a、for循环的范围只用标量值来表示;
b、for循环内部的每一条语句都要满足上面的两条规则,即只使用支持加速的数据类型,只使用三维以下的数组;c、循环内只调用了内建函数(build-infunction)。
4、当使用if、elseif、while和switch时,其条件测试语句中只使用了标量值时,将加速运行。
5、不要在一行中写入多条操作,这样会减慢运行速度。
即不要有这样的语句:
x= a.name;fork=1:
10000,sin(A(k)),end;
6、当某条操作改变了原来变量的数据类型或形状(大小,维数)时将会减慢运行速度。
7、应该这样使用复常量x=7+2i,而不应该这样使用:
x=7+2*i,后者会降低运行速度。
”
“尽量用向量化的运算来代替循环操作。
如将下面的程序:
i=0;
fort=0:
.01:
10
i=i+1;
y(i)=sin(t);
end
替换为:
t=0:
.01:
10;
y=sin(t);
速度将会大大加快。
最常用的使用vectorizing技术的函数有:
All、diff、ipermute、permute、reshape、ueeze、y、find、logical、prod、shiftdim、sub2ind、cumsum、ind2sub、ndgrid、repmat、sort、sum等。
”
“优先使用matlab内建函数,将耗时的循环编写进MEX-File中以获得加速。
b、使用Functions而不是Scripts。
”
“绝招:
你也许觉得下面两条是屁话,但有时候它真的是解决问题的最好方法。
1、改用更有效的算法
2、采用Mex技术,或者利用matlab提供的工具将程序转化为C语言、Fortran语言。
关于如何将M文件转化为C语言程序运行,可以参阅本版帖子:
“总结:
m文件转化为c/c++语言文件,VC编译”。
”
除了m程序提速的问题,这里还列出了《MATLAB代码矢量化指南(译)》
一、基本技术
-----------------------------------------------------
1)MATLAB索引或引用(MATLABIndexingorReferencing)
在MATLAB中有三种基本方法可以选取一个矩阵的子阵。
它们分别是下标法,线性法和逻辑法(subscripted,linear,andlogical)。
如果你已经熟悉这个内容,请跳过本节
1.1)下标法
非常简单,看几个例子就好。
A=6:
12;
A([3,5])
ans=
810
A([3:
2:
end])
ans=
81012
A=
[111417;
121518;
131619];
A(2:
3,2)
ans=
15
16
1.2)线性法
二维矩阵以列优先顺序可以线性展开,可以通过现行展开后的元素序号
来访问元素。
A=
[111417;
121518;
131619];
A(6)
ans=
16
A([3,1,8])
ans=
131118
A([3;1;8])
ans=
13
11
18
1.3)逻辑法
用一个和原矩阵具有相同尺寸的0-1矩阵,可以索引元素。
在某个位置上为1表示选取元素,否则不选。
得到的结果是一个向量。
A=6:
10;
A(logical([00101]))
ans=
810
A=
[12
34];
B=[1001];
A(logical(B))
ans=
14
-----------------------------------------------------
2)数组操作和矩阵操作(ArrayOperationsvs.MatrixOperations)
对矩阵的元素一个一个孤立进行的操作称作数组操作;而把矩阵视为一个整体进行的运算则成为矩阵操作。
MATLAB运算符*,/,\,^都是矩阵运算,而相应的数组操作则是.*,./,.\,.^
A=[10;01];
B=[01;10];
A*B%矩阵乘法
ans=
01
10
A.*B%A和B对应项相乘
ans=
00
00
------------------------------------------------------
3)布朗数组操作(BooleanArrayOperations)
对矩阵的比较运算是数组操作,也就是说,是对每个元素孤立进行的。
因此其结果就不是一个“真”或者“假”,而是一堆“真假”。
这个结果就是布朗数组。
D=[-0.21.01.53.0-1.04.23.14];
D>=0
ans=
0111011
如果想选出D中的正元素:
D=D(D>0)
D=
1.00001.50003.00004.20003.1400
除此之外,MATLAB运算中会出现NaN,Inf,-Inf。
对它们的比较参见下例
Inf==Inf返回真
Inf<1返回假
NaN==NaN返回假
同时,可以用isinf,isnan判断,用法可以顾名思义。
在比较两个矩阵大小时,矩阵必须具有相同的尺寸,否则会报错。
这是你用的上size和isequal,isequalwithequalnans(R13及以后)。
------------------------------------------------------
4)从向量构建矩阵(ConstructingMatricesfromVectors)
在MATLAB中创建常数矩阵非常简单,大家经常使用的是:
A=ones(5,5)*10
但你是否知道,这个乘法是不必要的?
A=10;
A=A(ones(5,5))
A=
1010101010
1010101010
1010101010
1010101010
1010101010
类似的例子还有:
v=(1:
5)';
n=3;
M=v(:
ones(n,1))
M=
111
222
333
444
555
事实上,上述过程还有一种更加容易理解的实现方法:
A=repmat(10,[55]);
M=repmat([1:
5]',[1,3]);
其中repmat的含义是把一个矩阵重复平铺,生成较大矩阵。
更多详细情况,参见函数repmat和meshgrid。
-----------------------------------------------------
5)相关函数列表(UtilityFunctions)
ones全1矩阵
zeros全0矩阵
reshape修改矩阵形状
repmat矩阵平铺
meshgrid3维plot需要用到的X-Y网格矩阵
ndgridn维plot需要用到的X-Y-Z...网格矩阵
filter一维数字滤波器,当数组元素前后相关时特别有用。
cumsum数组元素的逐步累计
cumprod数组元素的逐步累计
eye单位矩阵
diag生成对角矩阵或者求矩阵对角线
spdiags稀疏对角矩阵
gallery不同类型矩阵库
pascalPascal矩阵
hankelHankel矩阵
toeplitzToeplitz矩阵
==========================================================
二、扩充的例子
------------------------------------------------------
6)作用于两个向量的矩阵函数
假设我们要计算两个变量的函数F
F(x,y)=x*exp(-x^2-y^2)
我们有一系列x值,保存在x向量中,同时我们还有一系列y值。
我们要对向量x上的每个点和向量y上的每个点计算F值。
换句话说,我们要计算对于给定向量x和y的所确定的网格上的F值。
使用meshgrid,我们可以复制x和y来建立合适的输入向量。
然后
可以使用第2节中的方法来计算这个函数。
x=(-2:
.2:
2);
y=(-1.5:
.2:
1.5)';
[X,Y]=meshgrid(x,y);
F=X.*exp(-X.^2-Y.^2);
如果函数F具有某些性质,你甚至可以不用meshgrid,比如
F(x,y)=x*y,则可以直接用向量外积
x=(-2:
2);
y=(-1.5:
.5:
1.5);
x'*y
在用两个向量建立矩阵时,在有些情况下,稀疏矩阵可以更加有
效地利用存储空间,并实现有效的算法。
我们将在第8节中以一个
实例来进行更详细地讨论.
-----------------------------------------