如何加速MATLAB代碼運行

學習筆記編輯器

V1.0 2015/4/17ide

如何加速MATLAB代碼運行函數

 

概述工具

 

本文源於LDPCC的MATLAB代碼,即《CCSDS標準的LDPC編譯碼仿真》。因爲代碼的問題,在信息位長度很長(大於10000)狀況下,代碼沒法正常運行或執行速度很慢。本文將敘述代碼修改過程當中的一系列手段,然對其加速原理不作探究 性能

修訂歷史學習

如下表格展現了本文檔的修訂過程 測試

日期優化

版本號spa

修訂內容code

2015/04/17

V1.0

初始版本

 

簡介

 

本程序基於MATLAB 2014a 編寫,本文檔中提到的"MATLAB"均指該特定版本MATLAB。代碼運行結果測試機器是T530i i3 3110M 16G @1600MHz。

MATLAB的幫助文檔 - Advancde Software Development - Performance and Memory中說起了一些改善代碼性能的一些手段。比較通用的包括向量的預先分配內存,這一點在編輯器裏也會提示。有時候預先分配內存與否和性能關係很大,譬如

tic

x = 0;

for i = 2:1000000

x(i) = x(i-1)+5;

end

toc

tic

x = zeros(1,1000000);

for i = 2:1000000

x(i) = x(i-1)+5;

end

toc

運行結果顯示爲"時間已過 0.500985 秒"和"時間已過 0.073622 秒"。另外在聲明變量的時候不使用原有變量,而建立新變量也能夠減小運行時間。還有包括選擇'&'和'&&'的差別。

MATLAB還提供了一些改善性能的手段,包括

  • 將長腳本拆開成小段,調用執行;
  • 將大的代碼塊分開爲獨立的函數;
  • 將過度複雜的函數或是表達式採用簡單的來代替;
  • 採用函數,而不是腳本;
  • 向量化代碼,採用MATLAB自帶的函數;
  • 採用矩陣的稀疏結構;
  • 運行MATLAB的時候不要在後臺運行其餘大的程序;
  • 不要重載任何MATLAB的內建函數或數據類型。

 

不得不說上面的技巧有不少是廢話,而其中向量化是最有效的一種方法之一。向量化代碼中有不少經常使用的函數,包括

函數

功能(暫略)

all

 

any

 

cumsum

 

diff

 

find

 

ind2sub

 

ipermute

 

logical

 

meshgrid

 

ndgrid

 

permute

 

prod

 

repmat

 

reshape

 

shiftdim

 

sort

 

squeeze

 

sub2ind

 

sum

 

 

在代碼撰寫修改過程當中,能夠多考慮考慮以上函數。    

實例

 

實例來自於《CCSDS標準的LDPC編譯碼仿真》中代碼(實際上有點點差異),代碼優化從如下幾個方面進行

  • 稀疏
  • 類型轉換
  • 向量化

 

稀疏

仿真中的第一個困難在於ccsdscheckmatrix函數在輸入SIZE_M很大的時候,先不說運行時間,直接就爆內存了。(輸入參數4096,2/3)

先分析分析內存的問題,實際上這個函數的最後輸出結果就是一個矩陣,這個矩陣的大小是12288×28672,計算double型的內存佔用也就2G左右。可是函數運行過程當中產生了不少中間變量沒有清除。固然最後的解決辦法也沒有去管這些東西,因爲矩陣H是稀疏矩陣,因此之際採用sparse後,這個運行就沒有任何問題了。

對於矩陣H和H_sparse = spares(H),佔用內存以下(固然H要是稀疏的,否則得不償失)

Name     Size         Bytes      Class   Attributes

H       12288x28672   2818572288   double

H_sparse    12288x28672   1736712     double   sparse

也能夠對比稀疏矩陣和原始矩陣的運行時間(和稀疏程度有關)

代碼:tic;H*message';toc;

結果:時間已過 0.288934 秒。

代碼:tic;H_sparse*message';toc;

結果:時間已過 0.001210 秒。

類型轉換

MATLAB中的運算符支持多種類型,譬如矩陣乘法中多用double型變量,但若是一個矩陣是邏輯輸入也沒有關係。但運算速度差別較大,譬如

>> Gc_logic = Gc>0;

>> a=randi([0 1],1,16384);

>> tic;b = a*Gc;toc

時間已過 0.107618 秒。

>> tic;b = a*Gc_logic;toc

時間已過 0.503132 秒。

觀測結果類型爲double,咱們能夠大膽推測實際上邏輯型變量在運算過程當中先轉化爲了double型(邏輯怎麼乘呢?)另外一個實驗結果是

>> tic;Gc_logic=double(Gc_logic);b = a*Gc_logic;toc

時間已過 0.546412 秒。

這必定程度上證實了咱們的假設。因此在運算過程當中數據類型是重要的,若是上述乘法出如今循環內,那麼實現轉化矩陣類型是必要的。即便只運行一次,那麼顯式的轉化矩陣類型(特製新建變量)也有好處。譬如

>> tic;Gt=double(Gc_logic);b = a*Gt;toc

時間已過 0.373506 秒。

經過建立新變量,運行速度些許。

向量化

向量化其實是原代碼修改中獲益最大的方法,這其實是由於原先的譯碼程序寫了太多的循環。向量化後運行時間變成了原先的1/40 。固然,原先的代碼通用性強,而向量化這個過程其實是運用了H的一些結構的。譯碼函數太複雜,此處不作舉例。

此處分析差分調製中的例子(實際上對這個程序沒有什麼影響)

原來的代碼是這個樣子的(更新值爲其自己和前一個值的異或)

encodeData_extend = [1 encodeData];

for num = 2:length(encodeData_extend)

encodeData_extend(num) = xor(encodeData_extend(num),encodeData_extend(num-1));

end

向量化的結果爲(累加模二代替異或)

encodeData1 = [1 encodeData];

encodeData1_sum = cumsum(encodeData1);

encodeData_2 = mod(encodeData1_sum,2);

運行時間分別爲

時間已過 0.023424 秒。

時間已過 0.015003 秒。

雖而後者沒有快不少,但這取決於向量的長度,長度大的話會有較大差距。

 

其餘

MATLAB中說起的都能對代碼運行速度帶來細微的改進,包括

  • 將長腳本拆開成小段,調用執行;
  • 將大的代碼塊分開爲獨立的函數;
  • 將過度複雜的函數或是表達式採用簡單的來代替;
  • 採用函數,而不是腳本;

上述測試腳本(和以上運行條件有差異)

%% 稀疏矩陣測試
M=4096;
theta=[1    1    2    3    1    1    2    3    1    2    3    0    2    3    0    2    3    0    1    3    0    1    3    0    1    2];
fai=[1787    1077    1753    697    1523    5    2035    331    1920    130    4    85    551    15    1780    1960    3    145    1019    691    132    42    393    502    201    1064
1502    602    749    1662    1371    9    131    1884    1268    1784    19    1839    81    2031    76    336    529    74    68    186    905    1751    1516    1285    1597    1712
1887    521    590    1775    1738    2032    2047    85    1572    78    26    298    1177    1950    1806    128    1855    129    269    1614    1467    1533    925    1886    2046    1167
1291    301    1353    1405    997    2032    11    1995    623    73    1839    2003    2019    1841    167    1087    2032    388    1385    885    707    1272    7    1534    1965    588];
A = zeros(M);
B = eye(M);
L = 0:M-1;
for matrixNum = 1:14
    t_k = theta(matrixNum);
    f_4i_M = floor(4*L/M);
    f_k = fai(f_4i_M+1,matrixNum)';
    col_1 = M/4*(mod((t_k+f_4i_M),4)) + ...
        mod((f_k+L),M/4);
    row_col = col_1+1 + L*M;
    C_temp = zeros(M);
    C_temp(ind2sub([M,M],row_col)) = 1;
    C{matrixNum} = sparse(C_temp)';
end
H = [A A A B B+C{1};B+C{8} B+C{7}+C{6} A A B;A B B+C{5} A C{4}+C{3}+C{2}];
H_23 = [A A;B C{11}+C{10}+C{9};C{14}+C{13}+C{12} B];
H=[H_23 H];
H_full = full(H);
whos H H_full
%% 稀疏矩陣乘法測試
message = randi([0 1],1,28672);
tic;H*message';toc;
tic;H_full*message';toc;

%% 數據類型測試
Gc = randn(16384);
Gc_logic = Gc>0; 
a=randi([0 1],1,16384); 
tic;b = a*Gc;toc 
tic;b = a*Gc_logic;toc %邏輯型運行花費時間
tic;Gc_logic=double(Gc_logic);b = a*Gc_logic;toc %類型轉換
tic;Gt=double(Gc_logic);b = a*Gt;toc %創建新變量

%% 向量化測試
encodeData = randi([0 1],1,1000000);

tic;
encodeData_extend = [1 encodeData];
for num = 2:length(encodeData_extend)
    encodeData_extend(num) = xor(encodeData_extend(num),encodeData_extend(num-1));
end
toc;
tic;
encodeData1 = [1 encodeData];
encodeData1_sum = cumsum(encodeData1);
encodeData_2 = mod(encodeData1_sum,2);
toc;
View Code

 

profile

 

上一小節內容中有一句"實際上對這個程序沒有什麼影響",咱們怎麼判斷哪些代碼要修改,哪些代碼即便修改得再好對整個代碼運行也沒有什麼影響呢?三種方法

  • 感受(不可靠)
  • tic;toc;(太麻煩)
  • profile工具(很不錯)

profile的功能能夠help如下如何使用,我沒怎麼看,因此不怎麼會用……profile是用來分析代碼各個語句的運行時間的工具。使用方法是

  1. 輸入profile on
  2. 運行須要測試的代碼
  3. 輸入profile viewer

結果以下圖

點擊各個函數(腳本)能夠仔細觀測各個語句的運行狀態,由此來幫助優化MATLAB代碼,就像這樣

反正挺不錯的,但我不太會用就很少說了。

參考

 

MATLAB幫助

相關文章
相關標籤/搜索