2014-12-05 42 views
0

我有一個非常大的Matlab仿真項目,我想優化它,因爲我多次運行它來調整參數等。在大型仿真中改善Matlab功能

使用Matlab的profile我認定一個功能是吃了我大部分的時間,特別是output(i,1)= max(mean(dens(i+1:a,1)),dens(i+1,1));

該功能被稱爲LOT線,其中input10x1雙作爲參數傳遞,並output也是一個10x1載體。

function output = my_function(input) 

a = size(input,1); 
output = input*0; 
dens = density(input); 

% for each i, output(i) is the maximum between output(i+1) and mean(output(i+1:end)) 
for i = 1:a-1 
    output(i,1)= max(mean(dens(i+1:a,1)),dens(i+1,1)); 
end 
output(a,1) = dens(a,1); 

end 

我的想法:

  • 我覺得矢量將可能有助於擺脫循環的,但我不熟悉所有與技術(?)。
  • 是否有計算mean更快/替代方式(也許沒有Matlab的內置函數調用?)

編輯 我試圖向量化的功能,我得到了以下替換結果,其執行相同的操作:

function output = my_function_vectorized(input) 

a = size(input,1); 
rho_ref = zeros(size(input)); 
dens = density(input); 

temp_cumsum = flip(cumsum(flip(dens))./(1:1:a)'); 
output = [max(temp_cumsum(2:end),dens(2:a));dens(a)]; 

end 

我試圖測試二者功能通過以下方式:

Ts = random('unif',40,80,10,1000); 
Results_original = zeros(size(Ts)); 
Results_vectorized = zeros(size(Ts)); 
TIMES_original = zeros(size(Ts,2),1); 
TIMES_vectorized = zeros(size(Ts,2),1); 

for ii = 1:size(Ts,2) 
    tic; 
    Results_original(:,ii) = my_function(Ts(:,ii)); 
    TIMES_original(ii) = toc; 
end 

for ii = 1:size(Ts,2) 
    tic; 
    Results_vectorized(:,ii) = my_function_vectorized(Ts(:,ii)); 
    TIMES_vectorized(ii) = toc; 
end 

res = norm(Res_1 - Res_2); 
mTIMES_original = mean(TIMES_original); 
mTIMES_vectorized = mean(TIMES_vectorized); 

因我所得到:

res = 

    3.1815e-12 

mTIMES_original/mTIMEZ_vectorized = 

    3.0279 
  • 如果這種殘留被涉及到我?
  • 說我把這個計算結果固定爲3是否正確?

回答

2

向量化它。

重讀窩點是什麼在扼殺你,而不是中庸。意思是像Donald Knuth所能做到的那樣優化。

我不知道你的密度函數,所以我不知道我的索引。

僞剪:

%(1)faster predeclaration that shows intent 
output=zeroes(size(input)) 

%(2)vectorize your "mean between here and the end" 
b = fliplr(fliplr(cumsum(dens(1:a-1)))./fliplr(1:a-1)) 

%(3)assemble your interior nX2 matrix 
c = [b,dens] 

%(4)vectorized max, I think 
output = max(c,[],2) 

(1)它是很難被擊敗的內置插件的速度和效率。能夠從現在開始算出你的代碼的功能也很好。隨着時間的推移,我發現自己越來越成爲一個有文化程度的人(link),因爲從長遠來看,花費的時間比在一年或十年後花費的時間更少,並試圖對自己的工作進行逆向工程。 (2)這裏的想法是翻轉周圍的密度向量,然後進行累加和,然後將反向累加和的每個元素除以饋入其中的點數,然後再次翻轉它。當你用這個數字除以這個數字時 - 它就變成了一個數字。我只是閱讀了描述(鏈接),並且有一個內部開關,因此您可以在沒有觸發器的情況下重新編寫它,並使其更加快速。

b = cumsum(dens(1:a-1),'reverse')./(a-1:-1:1) %this might work 
在理論上這樣做時,你應該有一個矩陣,寬兩列,並且有許多行的「窩點」

(3)一樣。調整大小和預先宣佈可能會很昂貴 - 所以如果您經常更改大小,那麼您可能需要預先聲明它爲(1)。 (4)「最大」功能也將快速尖叫。不是你和Knuth先生都會加快速度。我認爲對於陣列的每個元素和少數洗牌(每個元素少於一個)比較(硅操作)都是需要的。

這是一個元素最大值。 (我忘了在中間添加緩衝區)。它已經很快了,它的輸出是一個數組。它可能需要一個1而不是2,但你知道你在那裏做什麼,並可以弄清楚。

讓我知道,如果這對你有用。我猜測它可能不會超過5倍的提高。

我驚訝地發現LabVIEW可以比MatLab做一些基礎快100倍,因爲它總是被編譯的。當在MatLab中編譯時,必須對類型和值施加許多新的約束,但在LV中,編譯通常是無痛的,因爲所有約束都是初始程序創建的一部分。如果你發現你的MatLab程序的核心速度不夠快,你可以爲LV做一個包裝,並且在很少(很多)的情況下運行它,而很少有心痛。 LV沒有做詳細說明 - 我們之所以用書本來代替圖片(或達芬奇話題的個性化渲染,是一個更正確的比喻)是有原因的。

編輯:(關於速度)

它看起來像你快了3倍。

編輯:(關於代碼,請注意我用2014A)

clc; format short g; 
a = 1:15 
mu = fliplr(cumsum(fliplr(a))./(1:length(a))) 

這給:

a = 

    1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 


mu = 

    Columns 1 through 9 

      8   8.5   9   9.5   10   10.5   11   11.5   12 

    Columns 10 through 15 

     12.5   13   13.5   14   14.5   15 

所以我做 「一」,一個矢量從1開始,並打算15 。最後一個值是15.第二個到最後一個值和最後一個值之間的平均值是14.5。最後3個值的平均值爲14.數學似乎在這裏工作。

編輯:

一個偉大的加速是切斷電流基於java系統。我見過的代碼通過版本2010A運行獲得了大量(優於3倍)的速度提升。通過Java運行時,某些代碼運行速度遠遠低於通過Fortran或基於C的編譯庫運行時的速度。

+0

謝謝,你能解釋一下你用最後一個'max'做什麼嗎? 我正在檢查,如果這可能在我的實施工作。另外,好的最後一段:) – 2014-12-05 19:15:38

+0

好吧,所以你的矢量化方法,**但**功能'cumsum'有一個主要問題。它將所有值顯示爲具有指數的向量:'1.0e + 03 *'整個向量。總而言之,我已經比較了手動爲每個「k」執行'sum(dens(k:a))'的值,它們是不同的。在這種情況下如何使'cumsum'正確工作? – 2014-12-06 11:05:12

+0

我在家使用「R」。答案將等到星期一,我再次訪問許可證付費的MatLab。 (可能在午餐時間) – EngrStudent 2014-12-06 22:10:31

2

正如已經建議,可以考慮向量化代碼;然而,實際上,我不確定在這種情況下真正提供了多少改進。首先,要記住的是,雖然在循環舊版本的MATLAB 被普遍認爲是非常低效相比,量化的方法,由於在現代的MATLAB JIT加速器,循環是沒有什麼大問題(性能明智),就像他們幾年前一樣。其次,考慮一下,如果你不得不跳過箍環來試圖將你的數據轉換成一個可以執行向量化命令的表單(這裏看起來可能是這種情況),那麼這可能是一個洗牌 - 這意味着執行矢量化命令所帶來的性能優勢超過了將數據操縱爲必要的矢量化形式所花費的時間(並可能使您的代碼徹底不可讀,面臨潛在錯誤並難以維護)。

這當然不是說矢量化對你的情況根本不會有幫助(唯一真正知道的方法是給它一個鏡頭並簡介它),但只是意識到潛在的限制。

除了由EngrStudent提出的建議,我也建議考慮看看文章 Accelerating MATLAB Algorithms and Applications從MathWorks公司。

特別是,這篇文章中描述的兩個選項在我身上跳出來,因爲它可能對您有幫助。

第一個是將您的函數轉換爲MATLAB可執行文件(MEX函數)。這是一個相當簡單的過程,包括使用MATLAB編碼器從您的函數自動生成C代碼,然後可以編譯爲可執行的MEX函數。我懷疑這提供了改善性能的最大潛力。 (如果你沒有 MATLAB編碼器工具箱,你也可以考慮手動編寫你的函數的C代碼版本(或至少它的時間密集型部分),並使用它到produce a MEX function,你可以在MATLAB中使用)。

第二種方法是利用並行計算。例如,因爲您的循環的每次迭代功能都彼此獨立運行,所以您可能會用並行循環(parfor)將其替換爲。另外,您的總體系統或工作流程的其他部分可能會並行化。這種方法顯然需要訪問 Parallel Computing Toolbox以及多核處理器(或集羣),所以這可能對您的使用有限......但如果您有權訪問這些資源,那麼這個可能對性能非常有利。