matlab需要多大运存_提高matlab运行效率
用過(guò)Matlab的人都知道,Matlab是一種解釋性語(yǔ)言,存在計(jì)算速度慢的問(wèn)題,為了提高程序的運(yùn)行效率,matlab提供了多種實(shí)用工具及編碼技巧。
1. 循環(huán)矢量化
Matlab是為矢量和矩陣操作而設(shè)計(jì)的,因此,可以通過(guò)矢量化方法加速M(fèi)文件的運(yùn)行。矢量化是指將for循環(huán)和while循環(huán)轉(zhuǎn)換為等價(jià)的矢量或矩陣操作。下面給出一個(gè)循環(huán)的例子:
i=0;
for n = 0:0.1:1000
i=i+1;
y(i)=cos(n);
end
那么我們可以矢量化為:
n= 0:0.1:1000;
y=cos(n);
我們可以用tic和toc函數(shù)來(lái)查看上述各代碼運(yùn)行的時(shí)間,采用for循環(huán)的程序0.39秒(具體時(shí)間和計(jì)算機(jī)配置有關(guān)),而矢量化后幾乎耗時(shí)為0。
2. 給數(shù)組或矩陣預(yù)分配內(nèi)存
特別是使用大型數(shù)組或矩陣時(shí),Matlab進(jìn)行動(dòng)態(tài)內(nèi)存分配和取消時(shí),可能會(huì)產(chǎn)生內(nèi)存碎片,這將導(dǎo)致大量閑置內(nèi)存產(chǎn)生,預(yù)分配可通過(guò)提前給大型數(shù)據(jù)結(jié)構(gòu)預(yù)約足夠空間來(lái)避免這個(gè)問(wèn)題。
3. 用函數(shù)代替腳本文件
因?yàn)槊看握{(diào)用MATLAB的腳本文件都需要將不必要的中間變量加載到內(nèi)存中,每執(zhí)行一次,就加載一次。函數(shù)在調(diào)用時(shí)被編譯成了偽代碼,只需要加載到內(nèi)存一次。當(dāng)多次調(diào)用同一個(gè)函數(shù)時(shí)會(huì)運(yùn)行快一些。因此盡量多使用函數(shù)文件而少使用腳本文件,也是提高執(zhí)行效率的一種方法。
4. 用Mex文件編寫(xiě)循環(huán)代碼
Matlab提供了與C和C++的接口,那么我們可以在用C或C++語(yǔ)言編寫(xiě)耗時(shí)的循環(huán)代碼,然后通過(guò)接口程序在Matlab中轉(zhuǎn)換成dll文件,這就是我們所要的Mex文件,通過(guò)這種方法可以極大地提高計(jì)算速率。
1.盡量避免使用循環(huán)結(jié)構(gòu)
MATLAB變量的基本類(lèi)型是矩陣,當(dāng)對(duì)矩陣的每個(gè)元素循環(huán)處理時(shí),運(yùn)算速度很慢。因此編程時(shí)應(yīng)盡量把數(shù)組和矩陣看作一個(gè)整體來(lái)進(jìn)行編程,而不是像其他的程序設(shè)計(jì)語(yǔ)言那樣,使用循環(huán)結(jié)構(gòu)對(duì)矩陣的元素循環(huán)進(jìn)行處理。利用MATLAB提供的用于矢量化操作的函數(shù),把循環(huán)矢量化,這樣既可以提高編程效率,也可以提高程序的執(zhí)行效率。下面給出一個(gè)循環(huán)的例子:
i=0;
for n = 0:0.1:100
i=i+1;
y(i)=cos(n)
end
上述程序段把數(shù)組中的每個(gè)元素都進(jìn)行函數(shù)值計(jì)算,這樣會(huì)耗費(fèi)大量的運(yùn)算時(shí)間,我們可以把數(shù)組看作一個(gè)整體來(lái)處理,計(jì)算函數(shù)值,可以修改這個(gè)程序段如下。
n = 0:0.1:100;
y = cos(n)
通過(guò)使用MATLAB專(zhuān)門(mén)提供的測(cè)試程序運(yùn)行時(shí)間的函數(shù),可以發(fā)現(xiàn),把數(shù)組看作一個(gè)整體,進(jìn)行操作后,執(zhí)行效率提高約300倍。
另外,在必須使用多重循環(huán)的情況下,建議在循環(huán)的外環(huán)執(zhí)行循環(huán)次數(shù)少的,內(nèi)環(huán)執(zhí)行循環(huán)次數(shù)多的,這樣也可以顯著提高程序執(zhí)行速度。
2.在使用數(shù)組或矩陣之前先定義維數(shù)
MATLAB中的變量在使用之前不需要明確地定義和指定維數(shù)。但當(dāng)未預(yù)定義數(shù)組或矩陣的維數(shù)時(shí),當(dāng)需賦值的元素下標(biāo)超出現(xiàn)有的維數(shù)時(shí),MATLAB?就為該數(shù)組或矩陣擴(kuò)維一次,這樣就會(huì)大大降低程序的執(zhí)行效率。因此,在使用數(shù)組或矩陣之前,預(yù)定義維數(shù)可以提高程序的執(zhí)行效率。
3.對(duì)矩陣元素使用下標(biāo)或者索引操作
在MATLAB中,矩陣元素的引用可用兩個(gè)下標(biāo)來(lái)表示。例如:A(i,j)
表示矩陣的第i行第j列的元素;A(1:k,j)表示矩陣A的第j列的前k個(gè)元素;A(:,j)
表示矩陣的第j列的所有元素。求矩陣A的第j列元素的平均值的表達(dá)式為mean(A(:,j))。
4.盡量多使用函數(shù)文件少使用腳本文件
因?yàn)槊看握{(diào)用MATLAB的腳本文件都需要將不必要的中間變量加載到內(nèi)存中,每執(zhí)行一次,就加載一次。函數(shù)在調(diào)用時(shí)被編譯成了偽代碼,只需要加載到內(nèi)存一次。當(dāng)多次調(diào)用同一個(gè)函數(shù)時(shí)會(huì)運(yùn)行快一些。因此盡量多使用函數(shù)文件而少使用腳本文件,也是提高執(zhí)行效率的一種方法。
5.在必須使用循環(huán)時(shí),可以考慮轉(zhuǎn)換為C-MEX
當(dāng)必須使用耗時(shí)的循環(huán)時(shí),可以考慮將循環(huán)體中的語(yǔ)句轉(zhuǎn)換為C-MEX。C-MEX是將M文件通過(guò)MATLAB的編譯器轉(zhuǎn)換為可執(zhí)行文件,是按照
MEX
技術(shù)要求的格式編寫(xiě)相應(yīng)的程序,通過(guò)編譯連接,生成擴(kuò)展名為.dll的動(dòng)態(tài)鏈接庫(kù)文件,可以在MATLAB環(huán)境下直接執(zhí)行。這樣,循環(huán)體中的語(yǔ)句在執(zhí)行時(shí)不必每次都解釋(interpret)。一般來(lái)說(shuō),C-MEX
文件的執(zhí)行速度是相同功能的M文件執(zhí)行速率的20~40倍。編寫(xiě)C-MEX不同于M文件,需要了解MATLAB?C-MEX規(guī)范。幸運(yùn)的是MATLAB提供了將M文件轉(zhuǎn)換為C-MEX的工具。
6.內(nèi)存優(yōu)化
MATLAB在進(jìn)行復(fù)雜的運(yùn)算時(shí)需要占用大量的內(nèi)存。合理使用內(nèi)存和提高內(nèi)存的使用效率,可以加快運(yùn)行速度,減少系統(tǒng)資源的占用。
7.內(nèi)存管理函數(shù)和命令
(1)Clear variablename:從內(nèi)存中刪除名稱(chēng)為variablename的變量。
(2)Clear all:從內(nèi)存中刪除所有的變量。
(3)Save:將指令的變量存入磁盤(pán)。
(4)Load:將save命令存入的變量載入內(nèi)存。
(5)Quit:退出MATLAB,并釋放所有分配的內(nèi)存。
(6)Pack:把內(nèi)存中的變量存入磁盤(pán),再用內(nèi)存中的連續(xù)空間載回這些變量。考慮到執(zhí)行效率問(wèn)題,不能在循環(huán)中使用。
8.節(jié)約內(nèi)存的方法
(1)避免生成大的中間變量,并刪除不再需要的臨時(shí)變量。
(2)當(dāng)使用大的矩陣變量時(shí),預(yù)先指定維數(shù)并分配好內(nèi)存,避免每次臨時(shí)擴(kuò)充維數(shù)。
(3)當(dāng)程序需要生成大量變量數(shù)據(jù)時(shí),可以考慮定期將變量寫(xiě)到磁盤(pán),然后清除這些變量。
當(dāng)需要這些變量時(shí),再重新從磁盤(pán)加載。
(4)當(dāng)矩陣中數(shù)據(jù)極少時(shí),將全矩陣轉(zhuǎn)換為稀疏矩陣。
提高M(jìn)ATLAB程序效率的幾點(diǎn)原則,這些都是俺在這兩年中參加四次數(shù)模編寫(xiě)大量m程序總結(jié)的經(jīng)驗(yàn),加之網(wǎng)上很多英雄也是所見(jiàn)略同。
1.“計(jì)算向量、矩陣化,盡量減少for循環(huán)。”[/B]
因?yàn)镸ATLAB本來(lái)就是矩陣實(shí)驗(yàn)室的意思,他提供了極其強(qiáng)大而靈活的矩陣運(yùn)算能力,你就沒(méi)必要自己再用自己編寫(xiě)的for循環(huán)去實(shí)現(xiàn)矩陣運(yùn)算的功能了。另外由于matlab是一種解釋性語(yǔ)言,所以最忌諱直接使用循環(huán)語(yǔ)句。但在有些情況下,使用for循環(huán)可以提高程序的易讀性,在效率提高不是很明顯的情況下可以選擇使用for循環(huán)。
口說(shuō)無(wú)憑,下面是利用tic與toc命令計(jì)算運(yùn)算所用時(shí)間的方法,測(cè)試兩種編程的效率。需要說(shuō)明的是沒(méi)有辦法精確計(jì)算程序執(zhí)行時(shí)間,matlab幫助這樣寫(xiě)到“Keep
in mind that tic and toc measure overall elapsed time. Make sure
that no other applications are running in the background on your
system that could affect the timing of your MATLAB
programs.”意思是說(shuō)在程序執(zhí)行的背后很可能有其他程序在執(zhí)行,這里涉及到程序進(jìn)程的的問(wèn)題,m程序執(zhí)行的過(guò)程中很可能有其他進(jìn)程中斷m程序來(lái)利用cup,所以計(jì)算出來(lái)的時(shí)間就不僅是m程序的了,在這種情況下我把那些寄點(diǎn)去掉,進(jìn)行多次計(jì)算求他的平均時(shí)間。
n = 100;
A(1:1000,1:1000) = 13;
C(1:1000,1) = 15;
D(1:1000,1) = 0;
for k = 1:n
D(:)
= 0;
tic
for
i = 1:1000
for
j = 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循環(huán)編程和矩陣化編程計(jì)算矩陣乘向量所用時(shí)間,u代表時(shí)間的比值。u(u<0) =
[];是認(rèn)為t1不可能小于t2,所以去掉不可能出現(xiàn)的情況。然后畫(huà)出圖形計(jì)算平均值。
經(jīng)多次試驗(yàn)結(jié)果大致相同,其中一次結(jié)果如下:
p =
9.6196
------------t1時(shí)間是t2的十倍左右。
2.“循環(huán)內(nèi)大數(shù)組預(yù)先定義--預(yù)先分配空間”[/U]
這一點(diǎn)原則是極其重要的,以至于在編寫(xiě)m程序時(shí)編輯器會(huì)給出提示“'ver' might be growing inside a
loop.Consider prealloacting for speed.”
clear
n = 50;
m = 1000;
for k = 1:n
A
= [];
tic
A(1:m,1:m)
= 3;
for
i = 1:m
A(i,i)
= i;
end
t1(k)
= toc;
%------------
A
= [];
tic
for
j = 1:m
A(j,j)
= j;
end
t2(k)
= toc;
end
t2(t1>10^9) = [];
t1(t1>10^9) = [];
plot([t1;t2]')
t1、t2分別表示預(yù)先分配空間和循環(huán)中分配空間的時(shí)間,下圖上面一條t2、下面t1
3.“盡可能利用matlab內(nèi)部提供的函數(shù)”[/U]
因?yàn)閙atlab內(nèi)部提供的函數(shù)絕對(duì)是各種問(wèn)題的最優(yōu)算法,那寫(xiě)程序都是他們大師級(jí)人物寫(xiě)出來(lái)的,程序應(yīng)該說(shuō)相當(dāng)高效,有現(xiàn)成的為什么不用那!?這個(gè)原則就不用實(shí)際的程序測(cè)試了。
關(guān)于MATLAB程序提速的問(wèn)題,可以參考網(wǎng)上很多智者的文章,都比較經(jīng)典。也可以看看我的上一篇文章,和網(wǎng)上大部分帖子有點(diǎn)不同,我是以實(shí)際的測(cè)試程序作為依據(jù)對(duì)如何提高M(jìn)ATLAB程序速度進(jìn)行介紹的。?這里我再補(bǔ)充幾點(diǎn)大家需要注意的。下面是我在國(guó)內(nèi)一個(gè)比較出名的論壇看到的關(guān)于m程序提速的帖子,開(kāi)始還真以為他們談?wù)摰亩紤?yīng)該遵循。(盡信書(shū)不如無(wú)書(shū))
帖子的一部分這樣說(shuō)道:“當(dāng)要預(yù)分配一個(gè)非double型變量時(shí)使用repmat函數(shù)以加速,如將以下代碼:
A = int8(zeros(100));
換成:
A = repmat(int8(0), 100, 100);”
凡事不能只憑自己的感覺(jué),沒(méi)有一點(diǎn)實(shí)際的例子,對(duì)于權(quán)威我們還要有挑戰(zhàn)精神那,就不用說(shuō)現(xiàn)在還不是經(jīng)典的觀點(diǎn)了。下面是我寫(xiě)的測(cè)試程序,我本來(lái)是想得到這位網(wǎng)友大哥的結(jié)果,但是實(shí)事不是我們想象的那么簡(jiǎn)單。
n = 100;
m = 1000;
for k=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)
可以看出下面的紅線(xiàn)是t1,而且最后的一句返回1,說(shuō)明兩種形式返回的值完全一樣。
由此我想說(shuō)的是,不管是在我們做論文,還是寫(xiě)博客的時(shí)候,別直接從網(wǎng)上或者別人文章那兒找點(diǎn)知識(shí)定理之類(lèi)的補(bǔ)充自己那蒼白無(wú)力的文章。最好是自己動(dòng)手編一下,“實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)”。
經(jīng)過(guò)這一測(cè)試,我感覺(jué)有必要,也有責(zé)任對(duì)這個(gè)論壇上的一大批經(jīng)典談?wù)摷右詼y(cè)試。盡管這個(gè)結(jié)論是錯(cuò)誤的但這還不足以證明論壇上的帖子都不是經(jīng)典。
還有一點(diǎn)關(guān)于m程序提速的這樣說(shuō)到:“在必須使用多重循環(huán)時(shí)下,如果兩個(gè)循環(huán)執(zhí)行的次數(shù)不同,則在循環(huán)的外環(huán)執(zhí)行循環(huán)次數(shù)少的,內(nèi)環(huán)執(zhí)行循環(huán)次數(shù)多的。這樣可以顯著提高速度。”
n=1000;
A = ones(1000)*13;
for k=1:n
tic
for
i=1:10
for
j=1:1000
A(i,j)=A(i,j)*15;
end
end
t1(k)=toc;
tic
for
i=1:1000
for
j=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)=[];%去除外界因素影響所產(chǎn)生的寄點(diǎn)
plot(1:size(t1,2),t1,'r',1:size(t1,2),t2)
由這個(gè)時(shí)間圖可以看出for循環(huán)的嵌套順序?qū)τ谒俣仁怯杏绊懙?#xff0c;雖然相對(duì)來(lái)說(shuō)差別不是很大,但是畢竟論壇上的觀點(diǎn)是正確的。至于他所說(shuō)的“顯著”二字就沒(méi)必要加上了。
此論壇還有一些提速的觀點(diǎn)列舉如下:
“遵守Performance Acceleration的規(guī)則
關(guān)于什么是“Performance Acceleration”請(qǐng)參閱matlab的幫助文件。我只簡(jiǎn)要的將
其規(guī)則總結(jié)如下7條:
1、只有使用以下數(shù)據(jù)類(lèi)型,matlab才會(huì)對(duì)其加速:
logical,char,int8,uint8,int16,uint16,int32,uint32,double
而語(yǔ)句中如果使用了非以上的數(shù)據(jù)類(lèi)型則不會(huì)加速,如:numeric,cell,structure,single,function
handle,java classes,user classes,int64,uint64
2、matlab不會(huì)對(duì)超過(guò)三維的數(shù)組進(jìn)行加速。
3、當(dāng)使用for循環(huán)時(shí),只有遵守以下規(guī)則才會(huì)被加速:a、for循環(huán)的范圍只用標(biāo)量值來(lái)表示;
b、for循環(huán)內(nèi)部的每一條語(yǔ)句都要滿(mǎn)足上面的兩條規(guī)則,即只使用支持加速的數(shù)據(jù)類(lèi)型,只使用三維以下的數(shù)組;c、循環(huán)內(nèi)只調(diào)用了內(nèi)建函數(shù)(build-in
function)。
4、當(dāng)使用if、elseif、while和switch時(shí),其條件測(cè)試語(yǔ)句中只使用了標(biāo)量值時(shí),將加速運(yùn)行。
5、不要在一行中寫(xiě)入多條操作,這樣會(huì)減慢運(yùn)行速度。即不要有這樣的語(yǔ)句:
x =?a.name; for k=1:10000, sin(A(k)), end;
6、當(dāng)某條操作改變了原來(lái)變量的數(shù)據(jù)類(lèi)型或形狀(大小,維數(shù))時(shí)將會(huì)減慢運(yùn)行速度。
7、應(yīng)該這樣使用復(fù)常量x = 7 + 2i,而不應(yīng)該這樣使用:x = 7 + 2*i,后者會(huì)降低運(yùn)行速度。”
“盡量用向量化的運(yùn)算來(lái)代替循環(huán)操作。如將下面的程序:
i=0;
for t = 0:.01:10
i
= i+1;
y(i)
= sin(t);
end
替換為:
t = 0:.01:10;
y = sin(t);
速度將會(huì)大大加快。最常用的使用vectorizing技術(shù)的函數(shù)有:All、diff、ipermute、permute、reshape、ueeze、y、find、logical、prod、shiftdim、sub2ind、cumsum、ind2sub、ndgrid、repmat、sort、sum
等。”
“優(yōu)先使用matlab內(nèi)建函數(shù),將耗時(shí)的循環(huán)編寫(xiě)進(jìn)MEX-File中以獲得加速。
b、使用Functions而不是Scripts 。”
“ 絕招:你也許覺(jué)得下面兩條是屁話(huà),但有時(shí)候它真的是解決問(wèn)題的最好方法。
1、改用更有效的算法
2、采用Mex技術(shù),或者利用matlab提供的工具將程序轉(zhuǎn)化為C語(yǔ)言、Fortran語(yǔ)言。關(guān)于如何將M文件轉(zhuǎn)化為C語(yǔ)言程序運(yùn)行,可以參閱本版帖子:“總結(jié):m文件轉(zhuǎn)化為c/c++語(yǔ)言文件,VC編譯”。
”
除了m程序提速的問(wèn)題,這里還列出了《MATLAB代碼矢量化指南(譯)》
一、基本技術(shù)?-----------------------------------------------------?1)MATLAB索引或引用(MATLAB Indexing or
Referencing)?在MATLAB中有三種基本方法可以選取一個(gè)矩陣的子陣。它們分別是 下標(biāo)法,線(xiàn)性法和邏輯法(subscripted,linear,
and logical)。?如果你已經(jīng)熟悉這個(gè)內(nèi)容,請(qǐng)?zhí)^(guò)本節(jié)
1.1)下標(biāo)法?非常簡(jiǎn)單,看幾個(gè)例子就好。?A = 6:12;?A([3,5])?ans =?8 10?A([3:2:end])ans
=?8 10 12
A =?[11 14 17;?12 15 18;
13 16 19];?A(2:3,2)?ans =?15?16
1.2)線(xiàn)性法?二維矩陣以列優(yōu)先順序可以線(xiàn)性展開(kāi),可以通過(guò)現(xiàn)行展開(kāi)后的元素序號(hào)?來(lái)訪(fǎng)問(wèn)元素。?A =?[11 14 17;
12 15 18;
13 16 19];?A(6)?ans =?16
A([3,1,8])ans
=?13 11 18?A([3;1;8])?ans =?13?11?18
1.3)邏輯法?用一個(gè)和原矩陣具有相同尺寸的0-1矩陣,可以索引元素。在某個(gè)位置上為1表示選取元素,否則不選。得到的結(jié)果是一個(gè)向量。?A = 6:10;?A(logical([0 0 1 0 1]))?ans =?8 10?A =?[1 2?3 4];?B = [1 0 0 1];?A(logical(B))?ans =?1 4?-----------------------------------------------------?2)數(shù)組操作和矩陣操作(Array Operations vs. Matrix
Operations)?對(duì)矩陣的元素一個(gè)一個(gè)孤立進(jìn)行的操作稱(chēng)作數(shù)組操作;而把矩陣視為一個(gè)整體進(jìn)行的運(yùn)算則成為矩陣操作。MATLAB運(yùn)算符*,/,\,^都是矩陣運(yùn)算,而相應(yīng)的數(shù)組操作則是.*,
./, .\, .^?A=[1 0 ;0 1];?B=[0 1 ;1 0];?A*B % 矩陣乘法?ans =?0 1?1 0?A.*B % A和B對(duì)應(yīng)項(xiàng)相乘?ans =?0 0?0 0?------------------------------------------------------?3)布朗數(shù)組操作(Boolean Array Operations)?對(duì)矩陣的比較運(yùn)算是數(shù)組操作,也就是說(shuō),是對(duì)每個(gè)元素孤立進(jìn)行的。因此其結(jié)果就不是一個(gè)“真”或者“假”,而是一堆“真假”。這個(gè)結(jié)果就是布朗數(shù)組。?D = [-0.2 1.0 1.5 3.0 -1.0 4.2 3.14];?D >= 0?ans =?0 1 1 1 0 1 1?如果想選出D中的正元素:?D = D(D>0)?D =?1.0000 1.5000 3.0000 4.2000 3.1400?除此之外,MATLAB運(yùn)算中會(huì)出現(xiàn)NaN,Inf,-Inf。對(duì)它們的比較參見(jiàn)下例?Inf==Inf返回真?Inf<1返回假?NaN==NaN返回假?同時(shí),可以用isinf,isnan判斷,用法可以顧名思義。在比較兩個(gè)矩陣大小時(shí),矩陣必須具有相同的尺寸,否則會(huì)報(bào)錯(cuò)。這是
你用的上size和isequal,isequalwithequalnans(R13及以后)。?------------------------------------------------------?4)從向量構(gòu)建矩陣(Constructing Matrices from
Vectors)?在MATLAB中創(chuàng)建常數(shù)矩陣非常簡(jiǎn)單,大家經(jīng)常使用的是:?A = ones(5,5)*10?但你是否知道,這個(gè)乘法是不必要的??A = 10;?A = A(ones(5,5))?A =?10 10 10 10 10?10 10 10 10 10?10 10 10 10 10?10 10 10 10 10?10 10 10 10 10?類(lèi)似的例子還有:?v = (1:5)';?n = 3;?M = v(:,ones(n,1))?M =
1 1 1?2 2 2?3 3 3?4 4 4?5 5 5?事實(shí)上,上述過(guò)程還有一種更加容易理解的實(shí)現(xiàn)方法:?A = repmat(10,[5 5]);?M = repmat([1:5]', [1,3]);?其中repmat的含義是把一個(gè)矩陣重復(fù)平鋪,生成較大矩陣。更多詳細(xì)情況,參見(jiàn)函數(shù)repmat和meshgrid。?-----------------------------------------------------?5)相關(guān)函數(shù)列表(Utility Functions)?ones 全1矩陣?zeros 全0矩陣?reshape 修改矩陣形狀?repmat 矩陣平鋪?meshgrid 3維plot需要用到的X-Y網(wǎng)格矩陣?ndgrid n維plot需要用到的X-Y-Z...網(wǎng)格矩陣?filter 一維數(shù)字濾波器,當(dāng)數(shù)組元素前后相關(guān)時(shí)特別有用。?cumsum 數(shù)組元素的逐步累計(jì)?cumprod 數(shù)組元素的逐步累計(jì)?eye 單位矩陣?diag 生成對(duì)角矩陣或者求矩陣對(duì)角線(xiàn)?spdiags 稀疏對(duì)角矩陣?gallery 不同類(lèi)型矩陣庫(kù)?pascal Pascal 矩陣?hankel Hankel 矩陣?toeplitz Toeplitz 矩陣
==========================================================?二、擴(kuò)充的例子?------------------------------------------------------?6)作用于兩個(gè)向量的矩陣函數(shù)?假設(shè)我們要計(jì)算兩個(gè)變量的函數(shù)F?F(x,y) = x*exp(-x^2 - y^2)?我們有一系列x值,保存在x向量中,同時(shí)我們還有一系列y值。我們要對(duì)向量x上的每個(gè)點(diǎn)和向量y上的每個(gè)點(diǎn)計(jì)算F值。換句話(huà)說(shuō),我們要計(jì)算對(duì)于給定向量x和y的所確定的網(wǎng)格上的F值。?使用meshgrid,我們可以復(fù)制x和y來(lái)建立合適的輸入向量。然后?可以使用第2節(jié)中的方法來(lái)計(jì)算這個(gè)函數(shù)。?x = (-2:.2:2);?y = (-1.5:.2:1.5)';?[X,Y] = meshgrid(x, y);?F = X .* exp(-X.^2 - Y.^2);?如果函數(shù)F具有某些性質(zhì),你甚至可以不用meshgrid,比如?F(x,y) = x*y ,則可以直接用向量外積?x = (-2:2);?y = (-1.5:.5:1.5);?x'*y?在用兩個(gè)向量建立矩陣時(shí),在有些情況下,稀疏矩陣可以更加有?效地利用存儲(chǔ)空間,并實(shí)現(xiàn)有效的算法。我們將在第8節(jié)中以一個(gè)?實(shí)例來(lái)進(jìn)行更詳細(xì)地討論.?--------------------------------------------------------?7)排序、設(shè)置和計(jì)數(shù)(Ordering, Setting, and Counting
Operations)?在迄今為止討論過(guò)的例子中,對(duì)向量中一個(gè)元素的計(jì)算都是獨(dú)立于同一向量的其他元素的。但是,在許多應(yīng)用中,你要做的計(jì)算則可能與其它元素密切相關(guān)。例如,假設(shè)你用一個(gè)向量x來(lái)表示一
個(gè)集合。不觀察向量的其他元素,你并不知道某個(gè)元素是不是一個(gè)冗余元素,并應(yīng)該被去掉。如何在不使用循環(huán)語(yǔ)句的情況下刪除冗余元素,至少在現(xiàn)在,并不是一個(gè)明顯可以解決的問(wèn)題。?解決這類(lèi)問(wèn)題需要相當(dāng)?shù)闹乔伞R韵陆榻B一些可用的基本工具
max 最大元素?min 最小元素?sort 遞增排序?unique 尋找集合中互異元素(去掉相同元素)?diff 差分運(yùn)算符[X(2) - X(1), X(3) - X(2), ... X(n) -
X(n-1)]?find 查找非零、非NaN元素的索引值?union 集合并?intersect 集合交?setdiff 集合差?setxor 集合異或
繼續(xù)我們的實(shí)例,消除向量中的多余元素。注意:一旦向量排序后,任何多余的元素就是相鄰的了。同時(shí),在任何相等的相鄰元素在向量diff運(yùn)算時(shí)變?yōu)榱恪_@是我們能夠應(yīng)用以下策略達(dá)到目的。我們現(xiàn)在在已排序向量中,選取那些差分非零的元素。
%
初次嘗試。不太正確!?x = sort(x(:));?difference = diff(x);?y = x(difference~=0);
這離正確結(jié)果很近了,但是我們忘了diff函數(shù)返回向量的元素個(gè)數(shù)比輸入向量少1。在我們的初次嘗試中,沒(méi)有考慮到最后一個(gè)元素也可能是相異的。為了解決這個(gè)問(wèn)題,我們可以在進(jìn)行差分之前給向量x加入一個(gè)元素,并且使得它與以前的元素一定不同。一種實(shí)現(xiàn)的方法是增加一個(gè)NaN。
% 最終的版本。x = sort(x(:));?difference = diff([x;NaN]);?y = x(difference~=0);
我們使用(:)運(yùn)算來(lái)保證x是一個(gè)向量。我們使用~=0運(yùn)算,而不用find函數(shù),因?yàn)閒ind函數(shù)不返回NaN元素的索引值,而我們操作中差分的最后元素一定是NaN。這一實(shí)例還有另一種實(shí)現(xiàn)方式:
y=unique(x);
后者當(dāng)然很簡(jiǎn)單,但是前者作為一個(gè)練習(xí)并非無(wú)用,它是為了練習(xí)使用矢量化技術(shù),并示范如何編寫(xiě)你自己的高效代碼。此外,前者還有一個(gè)作用:Unique函數(shù)提供了一些超出我們要求的額外功能,這可能降低代碼的執(zhí)行速度。?假設(shè)我們不只是要返回集合x(chóng),而且要知道在原始的矩陣?yán)锩總€(gè)相異元素出現(xiàn)了多少個(gè)“復(fù)本”。一旦我們對(duì)x排序并進(jìn)行了差分,我們可以用find來(lái)確定差分變化的位置。再將這個(gè)變化位置進(jìn)行差分,就可以得到復(fù)本的數(shù)目。這就是"diff
of find of diff"的技巧。基于以上的討論,我們有:
% Find the redundancy in a vector
x?x = sort(x(:));?difference = diff([x;max(x)+1]);?count = diff(find([1;difference]));?y = x(find(difference));?plot(y,count)
這個(gè)圖畫(huà)出了x中每個(gè)相異元素出現(xiàn)的復(fù)本數(shù)。注意,在這里我們避開(kāi)了NaN,因?yàn)閒ind不返回NaN元素的索引值。但是,作為特例,NaN和Inf
的復(fù)本數(shù)可以容易地計(jì)算出來(lái):
count_nans =
sum(isnan(x(:)));?count_infs = sum(isinf(x(:)));
另一個(gè)用于求和或者計(jì)數(shù)運(yùn)算的矢量化技巧是用類(lèi)似建立稀疏矩陣的方法實(shí)現(xiàn)的。這還將在第9節(jié)中作更加詳細(xì)的討論.?-------------------------------------------------------?8)稀疏矩陣結(jié)構(gòu)(Sparse Matrix Structures)
在某些情況下,你可以使用稀疏矩陣來(lái)增加計(jì)算的效率。如果你構(gòu)造一個(gè)大的中間矩陣,通常矢量化更加容易。在某些情況下,你可以充分利用稀疏矩陣結(jié)構(gòu)來(lái)矢量化代碼,而對(duì)于這個(gè)中間矩陣不需要大的存儲(chǔ)空間。?假設(shè)在上一個(gè)例子中,你事先知道集合y的域是整數(shù)的子集,?{k+1,k+2,...k+n};即,?y = (1:n) + k
例如,這樣的數(shù)據(jù)可能代表一個(gè)調(diào)色板的索引值。然后,你就可以對(duì)集合中每個(gè)元素的出現(xiàn)進(jìn)行計(jì)數(shù)(構(gòu)建色彩直方圖?譯者)。這是對(duì)上一節(jié)中"diff
of find of diff"技巧的一種變形。現(xiàn)在讓我們來(lái)構(gòu)造一個(gè)大的m x n矩陣A,這里m是原始x向量中的元素?cái)?shù),
n是集合y中的元素?cái)?shù)。?A(i,j) = 1 if x(i) = y(j)?0 otherwise
回想一下第3節(jié)和第4節(jié),你可能認(rèn)為我們需要從x和y來(lái)構(gòu)造矩陣A。如果當(dāng)然可以,但要消耗許多存儲(chǔ)空間。我們可以做得更好,因?yàn)槲覀冎?#xff0c;矩陣A中的多數(shù)元素為0,x中的每個(gè)元素對(duì)應(yīng)的行上只有一個(gè)值為1。?以下就是構(gòu)造矩陣的方法(注意到y(tǒng)(j) = k+j,根據(jù)以上的公式):?x = sort(x(:));?A = sparse(1:length(x), x+k, 1, length(x), n);
現(xiàn)在我們對(duì)A的列進(jìn)行求和,得到出現(xiàn)次數(shù)。?count = sum(A);?在這種情況下,我們不必明確地形成排序向量y,因?yàn)槲覀兪孪戎?y = 1:n + k.
這里的關(guān)鍵是使用數(shù)據(jù),(也就是說(shuō),用x控制矩陣A的結(jié)構(gòu))。由于x在一個(gè)已知范圍內(nèi)取整數(shù)值,我們可以更加有效地構(gòu)造矩陣。
假設(shè)你要給一個(gè)很大矩陣的每一列乘以相同的向量。使用稀疏矩陣,不僅可以節(jié)省空間,并且要比在第5節(jié)介紹的方法更加快速.
下面是它的工作方式:
F = rand(1024,1024);?x = rand(1024,1);?% 對(duì)F的所有行進(jìn)行點(diǎn)型乘法.?Y = F * diag(sparse(x));?% 對(duì)F的所有列進(jìn)行點(diǎn)型乘法.?Y = diag(sparse(x)) * F;
我們充分利用矩陣乘法算符來(lái)執(zhí)行大規(guī)模運(yùn)算,并使用稀疏矩陣以防止臨時(shí)變量變得太大。?--------------------------------------------------------?9)附加的例子(Additional Examples)?下面的例子使用一些在本技術(shù)手冊(cè)中討論過(guò)的方法,以及其它一些相關(guān)方法。請(qǐng)嘗試使用tic
和toc(或t=cputime和cputime-t),看一下速度加快的效果。?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?用于計(jì)算數(shù)組的雙重for循環(huán)。?使用的工具:數(shù)組乘法?優(yōu)化前:?A = magic(100);?B = pascal(100);?for j = 1:100?for k =
1:100;?X(j,k) = sqrt(A(j,k)) * (B(j,k) - 1);?end?end優(yōu)化后:?A = magic(100);?B = pascal(100);?X = sqrt(A).*(B-1);?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?用一個(gè)循環(huán)建立一個(gè)向量,其元素依賴(lài)于前一個(gè)元素使用的工具:FILTER, CUMSUM,
CUMPROD?優(yōu)化前:?A = 1;?L = 1000;?for i = 1:L?A(i+1) =
2*A(i)+1;?end?優(yōu)化后:?L = 1000;?A = filter([1],[1 -2],ones(1,L+1));?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?如果你的向量構(gòu)造只使用加法或乘法,你可使用cumsum或cumprod函數(shù)。?優(yōu)化前:?n=10000;?V_B=100*ones(1,n);?V_B2=100*ones(1,n);?ScaleFactor=rand(1,n-1);?for i = 2:n?V_B(i)
= V_B(i-1)*(1+ScaleFactor(i-1));?end?for i=2:n?V_B2(i)
= V_B2(i-1)+3;?end?優(yōu)化后:?n=10000;?V_A=100*ones(1,n);?V_A2 = 100*ones(1,n);?ScaleFactor=rand(1,n-1);?V_A=cumprod([100 1+ScaleFactor]);?V_A2=cumsum([100 3*ones(1,n-1)]);?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?向量累加,每5個(gè)元素進(jìn)行一次:?工具:CUMSUM , 向量索引?優(yōu)化前:?% Use an arbitrary vector, x?x = 1:10000;?y = [];?for n = 5:5:length(x)?y
= [y sum(x(1:n))];?end?優(yōu)化后(使用預(yù)分配):?x = 1:10000;?ylength = (length(x) - mod(length(x),5))/5;?% Avoid using ZEROS command during
preallocation?y(1:ylength) = 0;?for n = 5:5:length(x)?y(n/5)
= sum(x(1:n));?end?優(yōu)化后(使用矢量化,不再需要預(yù)分配):?x = 1:10000;?cums = cumsum(x);?y = cums(5:5:length(x));?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?操作一個(gè)向量,當(dāng)某個(gè)元素的后繼元素為0時(shí),重復(fù)這個(gè)元素:?工具:FIND, CUMSUM, DIFF?任務(wù):我們要操作一個(gè)由非零數(shù)值和零組成的向量,要求把零替換成為它前面的非零數(shù)值。例如,我們要轉(zhuǎn)換下面的向量:?a=2; b=3; c=5; d=15; e=11;?x = [a 0 0 0 b 0 0 c 0 0 0 0 d 0 e 0 0 0 0
0];?為:?x = [a a a a b b b c c c c c d d e e e e e
e];?解(diff和cumsum是反函數(shù)):?valind = find(x);?x(valind(2:end)) = diff(x(valind));?x = cumsum(x);?>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?將向量的元素累加到特定位置上?工具:SPARSE?優(yōu)化前:?% The values we are summing at designated
indices?values = [20 15 45 50 75 10 15 15 35 40
10];?% The indices associated with the values are summed
cumulatively?indices = [2 4 4 1 3 4 2 1 3 3 1];?totals = zeros(max(indices),1);?for n = 1:length(indices)?totals(indices(n))
= totals(indices(n)) + values(n);?end?優(yōu)化后:?indices = [2 4 4 1 3 4 2 1 3 3 1];?totals = full(sparse(indices,1,values));
注意:這一方法開(kāi)辟了稀疏矩陣的新用途。在使用sparse命令創(chuàng)建稀疏矩陣時(shí),它是對(duì)分配到同一個(gè)索引的所有值求和,而不是替代已有的數(shù)值。這稱(chēng)為"向量累加",是MATLAB處理稀疏矩陣的方式。
關(guān)于MATLAB的效率問(wèn)題,很多文章,包括我之前寫(xiě)的一些,主要集中在使用向量化以及相關(guān)的問(wèn)題上。但是,最近我在實(shí)驗(yàn)時(shí)對(duì)代碼進(jìn)行profile的過(guò)程中,發(fā)現(xiàn)在新版本的MATLAB下,for-loop已經(jīng)得到了極大優(yōu)化,而效率的瓶頸更多是在函數(shù)調(diào)用和索引訪(fǎng)問(wèn)的過(guò)程中。
由于MATLAB特有的解釋過(guò)程,不同方式的函數(shù)調(diào)用和元素索引,其效率差別巨大。不恰當(dāng)?shù)氖褂梅绞娇赡茉诒緛?lái)不起眼的地方帶來(lái)嚴(yán)重的開(kāi)銷(xiāo),甚至可能使你的代碼的運(yùn)行時(shí)間增加上千倍(這就不是多買(mǎi)幾臺(tái)服務(wù)器或者增加計(jì)算節(jié)點(diǎn)能解決的了,呵呵)。
下面通過(guò)一些簡(jiǎn)單例子說(shuō)明問(wèn)題。(實(shí)驗(yàn)選在裝有Windows Vista的一臺(tái)普通的臺(tái)式機(jī)(Core2 Duo 2.33GHz +
4GB Ram)進(jìn)行,相比于計(jì)算集群,
這可能和大部分朋友的環(huán)境更相似一些。實(shí)驗(yàn)過(guò)程是對(duì)某一個(gè)過(guò)程實(shí)施多次的整體進(jìn)行計(jì)時(shí),然后得到每次過(guò)程的平均時(shí)間,以減少計(jì)時(shí)誤差帶來(lái)的影響。多次實(shí)驗(yàn),在均值附近正負(fù)20%的范圍內(nèi)的置信度高于95%。為了避免算上首次運(yùn)行時(shí)花在預(yù)編譯上的時(shí)間,在開(kāi)始計(jì)時(shí)前都進(jìn)行充分的“熱身”運(yùn)行。)?函數(shù)調(diào)用的效率
一個(gè)非常簡(jiǎn)單的例子,把向量中每個(gè)元素加1。(當(dāng)然這個(gè)例子根本不需要調(diào)函數(shù),但是,用它主要是為了減少函數(shù)執(zhí)行本身的時(shí)間,突出函數(shù)解析和調(diào)用的過(guò)程。)
作為baseline,先看看最直接的實(shí)現(xiàn)
% input u: u is a 1000000 x 1 vectorv = u +
1;?這個(gè)過(guò)程平均需要0.00105 sec。
而使用長(zhǎng)期被要求盡量避免的for-loop?n = numel(u);% v = zeros(n, 1) has been pre-allocated.for i = 1 :
n?v(i)
= u(i) + 1;end
所需的平均時(shí)間大概是0.00110
sec。從統(tǒng)計(jì)意義上說(shuō),和vectorization已經(jīng)沒(méi)有顯著差別。無(wú)論是for-loop或者vectorization,每秒平均進(jìn)行約10億次“索引-讀取-加法-寫(xiě)入”的過(guò)程,計(jì)算資源應(yīng)該得到了比較充分的利用。
要是這個(gè)過(guò)程使用了函數(shù)調(diào)用呢?MATLAB里面支持很多種函數(shù)調(diào)用方式,主要的有m-function, function handle,
anonymous function, inline, 和feval,而feval的主參數(shù)可以是字符串名字,function
handle, anonymous function或者inline。
用m-function,就是專(zhuān)門(mén)定義一個(gè)函數(shù)
function y =
fm(x)?y
= x + 1;
在調(diào)用時(shí)?for i = 1 :
n?v(i)
= fm(u(i));end
function
handle就是用@來(lái)把一個(gè)function賦到一個(gè)變量上,類(lèi)似于C/C++的函數(shù)指針,或者C#里面的delegate的作用
fh = @fm;for i = 1 :
n?v(i)
= fh(u(i));end
anonymous function是一種便捷的語(yǔ)法來(lái)構(gòu)造簡(jiǎn)單的函數(shù),類(lèi)似于LISP, Python的lambda表達(dá)式
fa = @(x) x + 1;for i = 1 :
n?v(i)
= fa(u(i));end
inline function是一種傳統(tǒng)的通過(guò)表達(dá)式字符串構(gòu)造函數(shù)的過(guò)程
fi = inline('x + 1', 'x');for i = 1 :
n?v(i)
= fi(u(i));end
feval的好處在于可以以字符串方式指定名字來(lái)調(diào)用函數(shù),當(dāng)然它也可以接受別的參數(shù)。
v(i) = feval_r('fm', u(i));v(i) = feval_r(fh, u(i));v(i) = feval_r(fa,
u(i));?對(duì)于100萬(wàn)次調(diào)用(包含for-loop本身的開(kāi)銷(xiāo),函數(shù)解析(resolution),壓棧,執(zhí)行加法,退棧,把返回值賦給接收變量),不同的方式的時(shí)間差別很大:
m-function0.385 secfunction handle0.615 secanonymous function0.635
secinline function166.00 secfeval_r('fm', u(i))8.328 secfeval_r(fh,
u(i))0.618 secfeval_r(fa, u(i))0.652 secfeval_r(@fm, u(i))2.788
secfeval_r(@fa, u(i))34.689 sec
從這里面,我們可以看到幾個(gè)有意思的現(xiàn)象:
·?首先,調(diào)用自定義函數(shù)的開(kāi)銷(xiāo)遠(yuǎn)遠(yuǎn)高于一個(gè)簡(jiǎn)單的計(jì)算過(guò)程。直接寫(xiě)
u(i) = v(i) + 1 只需要?0.0011
秒左右,而寫(xiě)u(i) = fm(v(i))
則需要0.385秒,時(shí)間多了幾百倍,而它們干的是同一件事情。這說(shuō)明了,函數(shù)調(diào)用的開(kāi)銷(xiāo)遠(yuǎn)遠(yuǎn)大于for-loop自己的開(kāi)銷(xiāo)和簡(jiǎn)單計(jì)算過(guò)程——在不同情況可能有點(diǎn)差別,一般而言,一次普通的函數(shù)調(diào)用花費(fèi)的時(shí)間相當(dāng)于進(jìn)行了幾百次到一兩千次雙精度浮點(diǎn)加法。
·?使用function handle和anonymous
function的開(kāi)銷(xiāo)比使用普通的m-函數(shù)要高一些。這可能是由于涉及到句柄解析的時(shí)間,而普通的函數(shù)在第一次運(yùn)行前已經(jīng)在內(nèi)存中進(jìn)行預(yù)編譯。
·?inline
function的運(yùn)行時(shí)間則令人難以接受了,竟然需要一百多秒(是普通函數(shù)調(diào)用的四百多倍,是直接計(jì)算的十幾萬(wàn)倍)。這是因?yàn)閙atlab是在每次運(yùn)行時(shí)臨時(shí)對(duì)字符串表達(dá)式(或者它的某種不太高效的內(nèi)部表達(dá))進(jìn)行parse。
·?feval_r(fh,
u(i))和fh(u(i)),feval_r(fa,
u(i))和fa(u(i))的運(yùn)行時(shí)間很接近,表明feval在接受句柄為主參數(shù)時(shí)本身開(kāi)銷(xiāo)很小。但是,surprising的是 for
i = 1 :
n?v(i)
= feval_r(@fm, u(i));end比起fh = @fm;for i = 1 :
n?v(i)
= feval_r(fh, u(i));end慢了4.5倍 (前者0.618秒,后者2.788秒)。
for i = 1 :
n?v(i)
= feval_r(@(x) x + 1, u(i));end比起fa = @(x) x + 1;for i = 1 :
n?v(i)
= feval_r(fa, u(i));end竟然慢了53倍(前者0.652秒,后者34.689秒)。
由于在MATLAB的內(nèi)部實(shí)現(xiàn)中,function
handle的解析是在賦值過(guò)程中進(jìn)行的,所以預(yù)先用一個(gè)變量把句柄接下,其效果就是預(yù)先完成了句柄解析,而如果直接把@fm或者@(x) x
+
1寫(xiě)在參數(shù)列上,雖然寫(xiě)法簡(jiǎn)潔一些,但是解析過(guò)程是把參數(shù)被賦值到所調(diào)函數(shù)的局部變量時(shí)才進(jìn)行,每調(diào)用一次解析一次,造成了巨大的開(kāi)銷(xiāo)。
·?feval使用字符串作為函數(shù)名字時(shí),所耗時(shí)間比傳入句柄大,因?yàn)檫@涉及到對(duì)函數(shù)進(jìn)行搜索的時(shí)間(當(dāng)然這個(gè)搜索是在一個(gè)索引好的cache里面進(jìn)行(除了第一次),而不是在所有path指定的路徑中搜索。)
在2007年以后,MATLAB推出了arrayfun函數(shù),上面的for-loop可以寫(xiě)為 v =
arrayfun(fh, u)
這平均需要4.48 sec,這比起for-loop(需時(shí)0.615
sec)還慢了7倍多。這個(gè)看上去“消除了for-loop"的函數(shù),由于其內(nèi)部設(shè)計(jì)的原因,未必能帶來(lái)效率上的正效果。?元素和域的訪(fǎng)問(wèn)
除了函數(shù)調(diào)用,數(shù)據(jù)的訪(fǎng)問(wèn)方式對(duì)于效率也有很大影響。MATLAB主要支持下面一些形式的訪(fǎng)問(wèn):
·?array-index?A(i):
·?cell-index:?C{i};
·?struct
field:?S.fieldname
·?struct field
(dynamic):?S.('fieldname')這里主要探索單個(gè)元素或者域的訪(fǎng)問(wèn)(當(dāng)然,MATLAB也支持對(duì)于子數(shù)組的非常靈活整體索引)。
對(duì)于一百萬(wàn)次訪(fǎng)問(wèn)的平均時(shí)間
A(i) for a numeric array0.0052 secC{i} for a cell array0.2568
secstruct field0.0045 secstruct field (with dynamic name)1.0394
sec
我們可以看到MATLAB對(duì)于單個(gè)數(shù)組元素或者靜態(tài)的struct
field的訪(fǎng)問(wèn),可以達(dá)到不錯(cuò)的速度,在主流臺(tái)式機(jī)約每秒2億次(連同for-loop的開(kāi)銷(xiāo))。而cell
array的訪(fǎng)問(wèn)則明顯緩慢,約每秒400萬(wàn)次(慢了50倍)。MATLAB還支持靈活的使用字符串來(lái)指定要訪(fǎng)問(wèn)域的語(yǔ)法(動(dòng)態(tài)名字),但是,是以巨大的開(kāi)銷(xiāo)為代價(jià)的,比起靜態(tài)的訪(fǎng)問(wèn)慢了200倍以上。
關(guān)于Object-oriented Programming
MATLAB在新的版本中(尤其是2008版),對(duì)于面向?qū)ο蟮木幊烫峁┝藦?qiáng)大的支持。在2008a中,它對(duì)于OO的支持已經(jīng)不亞于python等的高級(jí)腳本語(yǔ)言。但是,我在實(shí)驗(yàn)中看到,雖然在語(yǔ)法上提供了全面的支持,但是matlab里面面向?qū)ο蟮男屎艿?#xff0c;開(kāi)銷(xiāo)巨大。這里僅舉幾個(gè)例子。
·object中的property的訪(fǎng)問(wèn)速度是3500萬(wàn)次,比struct
field慢了6-8倍。MATLAB提供了一種叫做dependent
property的屬性,非常靈活,但是,效率更低,平均每秒訪(fǎng)問(wèn)速度竟然低至2.6萬(wàn)次(這種速度基本甚至難以用于中小規(guī)模的應(yīng)用中)。
·object中method調(diào)用的效率也明顯低于普通函數(shù)的調(diào)用,對(duì)于instance
method,每百萬(wàn)次調(diào)用,平均需時(shí)5.8秒,而對(duì)于static
method,每百萬(wàn)次調(diào)用需時(shí)25.8秒。這相當(dāng)于每次調(diào)用都需要臨時(shí)解析的速度,而matlab的類(lèi)方法解析的效率目前還明顯偏低。
·MATLAB中可以通過(guò)改寫(xiě)subsref和subsasgn的方法,對(duì)于對(duì)象的索引和域的訪(fǎng)問(wèn)進(jìn)行非常靈活的改造,可以通過(guò)它創(chuàng)建類(lèi)似于數(shù)組的對(duì)象。但是,一個(gè)符合要求的subsref的行為比較復(fù)雜。在一個(gè)提供了subsref的對(duì)象中,大部分行為都需要subsref進(jìn)行調(diào)度,而默認(rèn)的較優(yōu)的調(diào)度方式將被掩蓋。在一個(gè)提供了subsref的類(lèi)中(使用一種最快捷的實(shí)現(xiàn)),object
property的平均訪(fǎng)問(wèn)速度竟然降到1萬(wàn)5千次每秒。建議[/U]
根據(jù)上面的分析,對(duì)于撰寫(xiě)高效MATLAB代碼,我有下面一些建議:
1.?雖然for-loop的速度有了很大改善,vectorization(向量化)仍舊是改善效率的重要途徑,尤其是在能把運(yùn)算改寫(xiě)成矩陣乘法的情況下,改善尤為顯著。
2.?在不少情況下,for-loop本身已經(jīng)不構(gòu)成太大問(wèn)題,尤其是當(dāng)循環(huán)體本身需要較多的計(jì)算的時(shí)候。這個(gè)時(shí)候,改善概率的關(guān)鍵在于改善循環(huán)體本身而不是去掉for-loop。
3.?MATLAB的函數(shù)調(diào)用過(guò)程(非built-in
function)有顯著開(kāi)銷(xiāo),因此,在效率要求較高的代碼中,應(yīng)該盡可能采用扁平的調(diào)用結(jié)構(gòu),也就是在保持代碼清晰和可維護(hù)的情況下,盡量直接寫(xiě)表達(dá)式和利用built-in
function,避免不必要的自定義函數(shù)調(diào)用過(guò)程。在次數(shù)很多的循環(huán)體內(nèi)(包括在cellfun,
arrayfun等實(shí)際上蘊(yùn)含循環(huán)的函數(shù))形成長(zhǎng)調(diào)用鏈,會(huì)帶來(lái)很大的開(kāi)銷(xiāo)。
4.?在調(diào)用函數(shù)時(shí),首選built-in function,然后是普通的m-file函數(shù),然后才是function
handle或者anonymous function。在使用function handle或者anonymous
function作為參數(shù)傳遞時(shí),如果該函數(shù)被調(diào)用多次,最好先用一個(gè)變量接住,再傳入該變量。這樣,可以有效避免重復(fù)的解析過(guò)程。
5.?在可能的情況下,使用numeric array或者struct array,它們的效率大幅度高于cell
array(幾十倍甚至更多)。對(duì)于struct,盡可能使用普通的域(字段,field)訪(fǎng)問(wèn)方式,在非效率關(guān)鍵,執(zhí)行次數(shù)較少,而靈活性要求較高的代碼中,可以考慮使用動(dòng)態(tài)名稱(chēng)的域訪(fǎng)問(wèn)。
6.?雖然object-oriented從軟件工程的角度更為優(yōu)勝,而且object的使用很多時(shí)候很方便,但是MATLAB目前對(duì)于OO的實(shí)現(xiàn)效率很低,在效率關(guān)鍵的代碼中應(yīng)該慎用objects。
7.?如果需要設(shè)計(jì)類(lèi),應(yīng)該盡可能采用普通的property,而避免靈活但是效率很低的dependent
property。如非確實(shí)必要,避免重載subsref和subsasgn函數(shù),因?yàn)檫@會(huì)全面接管對(duì)于object的接口調(diào)用,往往會(huì)帶來(lái)非常巨大的開(kāi)銷(xiāo)(成千上萬(wàn)倍的減慢),甚至使得本來(lái)幾乎不是問(wèn)題的代碼成為性能瓶頸。
總結(jié)
以上是生活随笔為你收集整理的matlab需要多大运存_提高matlab运行效率的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: xshell连接服务器失败_xshell
- 下一篇: mysql索引分析_MySQL索引分析和