轉(zhuǎn)自 https://www.digquant.com.cn/forum.php?mod=viewthread&tid=258
一酒朵、速度
1.1 For-循環(huán)
1、改變算法,多用矩陣運算(尤其是矩陣乘法),盡量減少for循環(huán);
2勒叠、減少for循環(huán)中的函數(shù)調(diào)用;
傳統(tǒng)觀點認為for-loop是影響性能的致命環(huán)節(jié)膏孟,讓我們來對此驗證:
tic
toc
Elapsed time is 0.000239 seconds.
tic
toc
for i=1:1000000
end
Elapsed time is 0.000050 seconds.
從上面的實驗結(jié)果可以得出以下結(jié)論:
1眯分、tic/toc語句的時間開銷可以忽略不計
2、for-loop語句本身的時間開銷也非常小柒桑,關(guān)鍵的影響效率的地方不在于循環(huán)本身弊决,而是在于循環(huán)的內(nèi)部。
3魁淳、tic/toc不一定要成對出現(xiàn)飘诗,一個tic后面可以有多個toc,但需要需要重新計時的時候界逛,要再次執(zhí)行tic昆稿。
4、toc的結(jié)果可以用變量接收下來息拜,如:
T=toc
T(k)=toc;
接下來我們就借助for循環(huán)溉潭,分析一下其他的各個影響效率的因素净响。
內(nèi)建函數(shù)
tic
for i=1:1000000
cos(0);
end
toc
Mean elapsed time is 0.032866 seconds.
m-函數(shù)
tic
for i=1:1000000
func(i);
end
toc
Mean elapsed time is 0.185556 seconds.
function func( ~ )
end
匿名函數(shù)
tic
for i=1:1000000
funca(i);
end
toc
Mean elapsed time is 0.561228 seconds.
funca=@(x)'';
內(nèi)聯(lián)函數(shù)
tic
for i=1:1000000
funci(i);
end
toc
Mean elapsed time is 19.5606 seconds.
funci=inline('','x');
從上面的實驗結(jié)果可以得出以下結(jié)論:
1、內(nèi)聯(lián)函數(shù)的調(diào)用時間開銷最小喳瓣,約為for-loop本身的10倍
2馋贤、m-函數(shù)的調(diào)用時間開銷約為內(nèi)聯(lián)函數(shù)的6倍,約為for-loop本身的60倍
3畏陕、匿名函數(shù)的調(diào)用時間開銷約為m-函數(shù)的3倍配乓,約為for-loop本身的187倍
4、內(nèi)聯(lián)函數(shù)的調(diào)用時間開銷過大惠毁,盡量不要在循環(huán)中使用
5犹芹、另外MEX-函數(shù)的調(diào)用時間開銷,理應(yīng)介于內(nèi)聯(lián)函數(shù)和m-函數(shù)之間
矩陣索引
tic
A=zeros(1000000,1);
for i=1:1000000
A(i)=i;
end
toc
Mean elapsed time is 0.007592 seconds.
tic
A=zeros(1000000,1);
for i=1:1000000
A(i,1)=i;
end
toc
Mean elapsed time is 0.007954 seconds.
tic
A=zeros(1000000,1);
for i=1:1000000
A(i:i,1)=i;
end
toc
Mean elapsed time is 0.663598 seconds.
tic
A=zeros(1000000,1);
for i=1:1000000
A(i,:)=i;
end
toc
Mean elapsed time is 0.273345 seconds.
tic
A=zeros(1000000,1);
for i=1:1000000
A(i,1:1)=i;
end
toc
Mean elapsed time is 0.730042 seconds.
tic
A=zeros(1000000,1);
for i=1:1000000
A(i:i,1:1)=i;
end
toc
Mean elapsed time is 1.00852 seconds.
二鞠绰、內(nèi)存
2.1 內(nèi)存分配
tic
A=zeros(1000000,1);
for i=1:1000000
A(i)=i;
end
toc
Mean elapsed time is 0.009025 seconds.
tic
% A=zeros(1000000,1);
for i=1:1000000
A(i)=i;
end
toc
Mean elapsed time > 20 minutes.
因此羽莺,如果不預(yù)先分配好內(nèi)存,將會大大增加仿真時間洞豁,拖慢執(zhí)行效率。
所幸的是荒给,由于這個現(xiàn)象的重要性丈挟,Matlab的編輯器能夠發(fā)現(xiàn)并提示這個問題,會用紅的波浪線~
標記出來志电。
三曙咽、向量化
N=0:0.1:1000;
for i=0:10000
y(i)=cos(N(i));
end
向量化:
N=0:0.1:1000;
y=cos(N);
MATLAB向量化函數(shù)
accumarray函數(shù)
arrayfun函數(shù)
bsxfun函數(shù)
cellfun函數(shù)
spfun函數(shù)
3.1 accumarray函數(shù)
A = accumarray(subs,val)
A = accumarray(subs,val,sz)
A = accumarray(subs,val,sz,fun)
A = accumarray(subs,val,sz,fun,fillval)
A = ccumarray(subs,val,sz,fun,fillval,issparse)
val = 101:105;
subs = [1; 2; 4; 2; 4]
A = accumarray(subs, val)
A =
101
206
0
208
subs =
1 1 1
2 1 2
2 3 2
2 1 2
2 3 2
val =
101
102
103
104
105
1挑辆、val的元素個數(shù)與subs的行數(shù)是一致的例朱。
2、A = accumarray(subs, val)
的實現(xiàn)過程分成2步鱼蝉。
第一步
是把val中的元素洒嗤,按照subs對應(yīng)行所給出的下標放到一個新的cell矩陣B中(cell是為了方便解釋,也就是說B矩陣中的每個位置可以放入多個數(shù)值)魁亦,注意渔隶,subs的值是B的下標,不是val的洁奈。舉例來說间唉,subs第一行[ 1 1 1],意思就是把val中第一個元素(val(1))放入到B(1,1利术,1)的位置呈野,依次類推,val(2)放入到B(2 1 2)印叁,val(3)放入到B(2 3 2)被冒,val(4)放入到B(2 1 2)军掂,val(5)放入到B(2 3 2)。此時姆打,可以看到B(1良姆,1,1)中有1個數(shù)(val(1))幔戏;B(2 1 2)有2個數(shù)(val(2)玛追,val(4));B(2 3 2)也有2個數(shù)(val(3)闲延,val(5))痊剖。
第二步
把B中每個單元中的數(shù)分別累加,并放入到A的對應(yīng)位置垒玲。
注:accumarray
默認的是把每個單元中的數(shù)累加陆馁,因為對每個單元中的數(shù)的默認處理函數(shù)是sum『嫌可以通過A = accumarray(subs,val,[],[@fun](https://github.com/fun "@fun"))
的調(diào)用格式來指定其他的處理函數(shù)叮贩,比如說mean。對指定的fun函數(shù)的要求是佛析,接受列向量輸入益老,輸出單個的數(shù)值型,字符型或邏輯型變量寸莫。A的維數(shù)與B相同捺萌,A中的元素默認為零。A的大小為max(subs(1))×max(subs(2))×max(subs(3))…
很顯然膘茎,A的維數(shù)與subs的列數(shù)相等桃纯。
A = accumarray(subs, val)
A = accumarray(subs,val,sz)
sz 可以用來指定A大小,但是不能小于
A = accumarray(subs, val
)得到的A的大小披坏。比如A = accumarray(subs, val)
的到A是一個3×4的二維矩陣态坦,那么sz應(yīng)當(dāng)為一個包含2個元素的向量sz=[m1,m2] (sz向量的長度和A的維數(shù)相等),其中棒拂,m1大于等于3驮配,m2大于等于4. 但是,當(dāng)?shù)玫降腁是一個p×1的一維向量時着茸,sz=[m壮锻,1],m大于等于p涮阔。另外猜绣,sz可以賦值為空,表示由函數(shù)自動決定A的大小敬特。A = accumarray(subs,val,sz,fun)
fun可以指定專門的處理函數(shù)掰邢,默認的處理函數(shù)為sumA = accumarray(subs,val,sz,fun,fillval)
fillval指定A中元素的默認值牺陶。可以等于NaNA = ccumarray(subs,val,sz,fun,fillval,issparse)
isspares選擇A是否使用稀疏矩陣的格式A = accumarray({subs1, subs2, ...}, val,...)
{subs1, subs2, …}辣之,等同于A = accumarray(subs, val,…)掰伸,此時,subs=[subs1, subs2, …]或者=[subs1怀估;subs2狮鸭; …]
例子:
1000人,身高分布在170180cm多搀,體重在110100斤歧蕉,年齡分布在20~50歲,計算身高體重都相等的人的年齡平均值康铭。結(jié)果用矩陣來表示:行數(shù)表示身高惯退,列數(shù)表示體重,矩陣元素表示年齡的平均值从藤。
height=unidrnd(10,1000,1)+170; %身高的數(shù)據(jù)
weight=unidrnd(90,1000,1)+110; %體重的數(shù)據(jù)
old=unidrnd(30,1000,1)+20;
mo=accumarray([height,weight],old,[],@mean);
%或
mo=accumarray([height,weight],old,[],@mean,0,true);
3.2 arrayfun函數(shù)
arrayfun函數(shù)實現(xiàn)的是將指定的函數(shù)應(yīng)用到給定數(shù)組在內(nèi)的所有元素催跪。這樣以前不可避免的循環(huán)現(xiàn)在可以向量化了。
生成一個這樣的n×n矩陣
a:a(i,j)=dblquad(@(u,v)sin(u)*sqrt(v),0,i,0,j)夷野,以n=10為例叠荠。
a=zeros(10);
for ii=1:10
for jj=1:10
a(ii,jj)=dblquad(@(u,v) sin(u)+sqrt(v),0,ii,0,jj);
end
End
%現(xiàn)在只需要如下調(diào)用
[J,I]=meshgrid(1:10);
a1=arrayfun(@(ii,jj) dblquad(@(u,v) sin(u)+sqrt(v),0,ii,0,jj),I,J);
3.3 bsxfun函數(shù)
以前,當(dāng)我們想對一個矩陣A的每一列或每一行與同一個向量a進行某些操作(比較大小扫责、乘除等)時,只能用循環(huán)方法或者利用repmat函數(shù)將要操作的向量a復(fù)制成和A一樣尺寸的矩陣逃呼,進而進行操作鳖孤。從Matlab R2007a開始,有了更有效的方法抡笼,那就是bsxfun函數(shù)苏揣。
有如下矩陣:
向量為b=[1 2 3]T,請找出b在A矩陣列中的位置loc=[1,4]推姻。
方法1:
A=[1 2 3 1;2 3 4 2;3 3 8 3]
b=[1;2;3]
loc = find(all(bsxfun(@eq,A,b)))
%把A的每一列和b用==(@eq)來判斷平匈,找出全1的列;
方法2:
loc = find(arrayfun(@(n)all(A(:,n)==b),1:4))
%用arrayfun對n進行1:4的遍歷藏古;
方法3:
loc =find(all(~bsxfun(@minus,A,b)))
%把A的每一列和b來相減( @minus )增炭,求反后找出全1的列;
方法4:
loc=find( arrayfun(@(n) isequal(A(:,n),b),1:4))
%用arrayfun對n進行1:4的遍歷后用isequal函數(shù)來判斷A的每列和b是否相等拧晕;
方法5:
loc=find(b'*A==sum(b.^2))
%的轉(zhuǎn)置和A相乘隙姿,然后和b.^2每列的和進行比較,找到相等的;
3.4 cellfun函數(shù)
A={'Hello', 'MATLAB', 'I love MATLAB', 'MATLAB is powerful', 'MATLAB is the language of technical computer'}
A={‘Hello’, ‘MATLAB’, ‘I love MATLAB’, ‘MATLAB is powerful’, ‘MATLAB is the language of technical computer’};
cellfun(@length,A)
ans =
5 6 13 18 44
3.5 spfun函數(shù)
a=sparse([1 3 20 60 100],[2 20 30 60 80],1:5)
</pre>
a =
(1,2) 1
(3,20) 2
(20,30) 3
(60,60) 4
(100,80) 5
sa=spfun(@(x) x.^2+1,a)
</pre>
sa =
(1,2) 2
(3,20) 5
(20,30) 10
(60,60) 17
(100,80) 26
四厂捞、函數(shù)化
- 盡量使用內(nèi)建函數(shù)输玷,內(nèi)建函數(shù)的速度是最快的队丝。
- m-函數(shù)的執(zhí)行效率也很高。
- MEX-函數(shù)的執(zhí)行效率僅次于內(nèi)建函數(shù)欲鹏,將耗時的代碼寫成MEX-函數(shù),將大大提高運行速度机久。
- 匿名函數(shù),內(nèi)聯(lián)函數(shù)赔嚎,以及一些面向?qū)ο蠓椒ū旄牵M量不要在執(zhí)行次數(shù)多的循環(huán)體內(nèi)使用。
五尽狠、預(yù)分配內(nèi)存
A= zeros(1000, 1);
A = int8(zeros(100, 1));
A = zeros(1000, 1, 'int8');
常用的預(yù)分配內(nèi)存函數(shù):
- zeros
- ones
- eye