2013-07-10 32 views
5

我在MATLAB中用兩種不同的方式編寫了一些代碼。首先,我用兩個for循環,這似乎乍一看愚蠢:爲什麼(在MATLAB中)這段代碼更快?

Initial = [zeros(10,1) ones(10,1)]; 

for xpop=1:10 
    for nvar=1:10 
     Parent(xpop,nvar) = Initial(nvar,1)+(Initial(nvar,2)-Initial(nvar,1))*rand(); 
    end 
end 

在第二個方案中,我試圖做量化的計算(我假設它可以更快):

Parent = repmat(Initial(:,1),1,10) + rand(10,10).*(repmat(Initial(:,2),1,10)-repmat(Initial(:,1),1,10)); 

代碼的三種不同運行所耗用的時間可以看出如下:

Elapsed time is 0.000456 seconds. 
Elapsed time is 0.006342 seconds. 

Elapsed time is 0.000457 seconds. 
Elapsed time is 0.006147 seconds. 

Elapsed time is 0.000471 seconds. 
Elapsed time is 0.006433 seconds. 

爲什麼第一個方案比第二個方案更快? '。*'命令中是否真的做了兩個愚蠢的循環?

+4

我希望當'Initial'變大時它可能會改變。 –

+2

如果使用1000的維數而不是10的維數,您會看到相反的結果。 – tashuhka

+0

是的,這是正確的人感謝 – NKN

回答

9

您的測試設置太小,無法顯示矢量化的優勢。

Initial = [zeros(10,1) ones(10,1)]; 
Elapsed time is 0.000078 seconds. 
Elapsed time is 0.000995 seconds. 

現在對於一個更大的問題:

Initial = [zeros(1000,1) ones(1000,1)]; 
Elapsed time is 2.797949 seconds. 
Elapsed time is 0.049859 seconds. 
+1

因此總結,在某些情況下(小向量)循環會更快,但其他情況下(更大的向量)矢量化將更快。有一個已知的大小,其中兩個會聚,從而導致知識的門檻更快? – KronoS

+5

我想說_heavily_取決於所執行的確切操作。 – Christoph

+2

@KronoS在實踐中,使用一個循環擊敗矢量化等價物很少會使您的總代碼運行時間縮短一秒。另一方面,良好的矢量化通常可以節省數秒或更長時間。因此,除非您正在優化稱爲大量次的事情,否則不要打擾測試循環以獲得性能。 –

3

這是對你有好處來測試這些東西。但是,你需要學習如何做這些測試以獲得良好的信息。

首先,所花費的時間非常少,所以重複測試總是最好的。其次,使用像timeit這樣的工具。它爲您完成所有工作,消除了許多錯誤來源,儘管它需要將其目標封裝爲函數。

接下來,存在TINY問題。你的測試案例非常小。事實上,代碼花費時間有很多原因。考慮功能開銷和啓動成本。一個函數需要一段時間才能調用,因爲設置和銷燬函數工作區的開銷很大。此外,一個好功能將有錯誤測試,並提供幾個選項。但要做到這一點,它必須檢查是否設置了這些選項。所以花了時間,往往沒有任何價值,因爲你只是想用一些簡單的形式來使用函數。這意味着當你調用函數來向量化一個微小的計算時,它實際上可能需要更多的時間,比如果你只是使用unvectorized form inline。所以小的測試案例往往會引起誤解。 (我將增加一個更大的問題的時間比較,但到那時Marc已經在他的答案中這麼做了,請參閱背心差異以瞭解更大的問題。)

您還應該學會使用bsxfun,優化您正在測試的表單的某些計算。再次,小問題往往不會顯示出很大的速度增益,如果有的話。

接下來,JIT存在一些問題,即在MATLAB中加速以優化某些簡單代碼。如果那個(你看不見的)工具能夠很好地處理你正在測試的代碼,那麼它就會看起來好像循環更快。

這是很好做一些測試,所以讓我們做一個比較。由於你的例子都是主要內聯的,所以我只是圍繞每個案例做一個大循環。這將減少測試錯誤的主要來源之一。

Ktot = 100; 
N = 10; 
Initial = [zeros(N,1) ones(N,1)]; 

tic 
for k = 1:Ktot 
    for xpop=1:N 
    for nvar=1:N 
     Parent(xpop,nvar) = Initial(nvar,1)+(Initial(nvar,2)-Initial(nvar,1))*rand(); 
    end 
    end 
end 
toc 

tic 
for k = 1:Ktot 
    Parent = repmat(Initial(:,1),1,N) + rand(N,N).*(repmat(Initial(:,2),1,N)-repmat(Initial(:,1),1,N)); 
end 
toc 

你能改善你的矢量化表單嗎?爲什麼兩個repmats,當一個會工作?

tic 
for k = 1:Ktot 
    Parent = repmat(Initial(:,1),1,N) + rand(N,N).*repmat(Initial(:,2)-Initial(:,1),1,N); 
end 
toc 

bsxfun怎麼樣?

tic 
for k = 1:Ktot 
    Parent = bsxfun(@plus,Initial(:,1),bsxfun(@times,rand(N,N),Initial(:,2)-Initial(:,1))); 
end 
toc 

所以,用N = 10和Ktot = 100,我看到倍這樣的:

Elapsed time is 0.003935 seconds. 
Elapsed time is 0.012250 seconds. 
Elapsed time is 0.008269 seconds. 
Elapsed time is 0.004304 seconds. 

再次,這是一個小問題。如果我們擴大問題會發生什麼?嘗試N = 100,而不是N = 10.

Elapsed time is 0.131186 seconds. 
Elapsed time is 0.031671 seconds. 
Elapsed time is 0.027205 seconds. 
Elapsed time is 0.019763 seconds. 

所以我們看到了一些事情在邏輯上更加整理一些。現在bsxfun變體開始顯示出一些收益。接下來,去到N = 1000

Elapsed time is 12.288608 seconds. 
Elapsed time is 3.412531 seconds. 
Elapsed time is 2.690691 seconds. 
Elapsed time is 1.626599 seconds. 

從本質上講,所有這些代碼做同樣的工作,它只是一些在他們如何組織的問題更加有效,而一些有更多的開銷。正如我們在更大的問題中看到的那樣,顯式循環是平坦的。