1. 程式人生 > >Matlab的for迴圈優化

Matlab的for迴圈優化

因為學習和工作的原因,最近又開始使用已經許久沒有接觸的Matlab。在沒有什麼特殊考慮的情況下,信手寫下了下面的m程式碼片段:

複製程式碼  1 for i=1:1:(imgHeight-tmpHeight+1)
 2 for j=1:1:(imgWidth-tmpWidth+1)
 3         temp=0;
 4 for m=1:1:tmpHeight
 5 for n=1:1:tmpWidth
 6                 temp=temp+img(i+m-1,j+n-1)*template(m,n);
 7 end 8 end 9 if temp>010             tmpRst(i
+floor(tmpHeight/2),j+floor(tmpWidth/2))=temp;
11 end;
12 end13 end 複製程式碼

  外層迴圈的2個變數長度為300和400,內層的兩個為9。出乎我的意料的是,這樣一段程式碼在我的機器上([email protected], 2GB DDRII667)竟然要跑1分多鐘,而這段程式碼轉換為C++後是準備要在一個實時影象識別系統上跑的。換言之,時間至少必須下降到1/25以內!雖然可以指望C++的效率,但Matlab這樣的速度也太離譜了!況且我在Matlab中還要不斷的實驗,跑一遍就要1、2分鐘,實在令人難以接受。下午和師兄們討論時,無意談到了這個問題,他們立即對我說,想辦法轉換為矩陣操作什麼的,Matlab中for迴圈的效率是很低的!

   回寢室後,試驗了一下,結果令人乍舌,我把程式碼改成了下面的樣子(注意第三行程式碼實際上代替了內層for迴圈,其他的改動我想沒什麼本質影響):

複製程式碼 1 for i=1:1:(imgHeight-tmpHeight+1)
2 for j=1:1:(imgWidth-tmpWidth+1)
3         temp=img(i:(i+tmpHeight-1),j:(j+tmpWidth-1)).*template;
4         temp=sum(sum(temp));
5         tmpRst(i+floor(tmpHeight/2),j+floor(tmpWidth/2))=(temp
+abs(temp))/2;
6 end7 end 複製程式碼

  結果時間呢,只要了2秒左右!這麼一改,效率提升了幾十倍!我是學過一些編譯原理的,但這種差距實在令我感到很不解。求助Google後,算是得到了滿意的答覆。

  在前言中,文章中有兩段話:

   MATLAB programs are interpretted. This would seem to make it inapproapriate for large scale scientific computing. The power of MATLAB is realized with its extensive set of libraries which are compiled or are carefully coded in MATLAB to utilize "vectorization". The concept of vectorization is central to understanding how to write efficient MATLAB code.
  Vectorized code takes advantage, wherever possible, of operations involving data stored as vectors. This even applies to matrices since a MATLAB matrix is stored (by columns) in contiguous locations in the computer's RAM. The speed of a numerical algorithm in MATLAB is very sensitive to whether or not vectorized operations are used.

  其核心大意就是說為了彌補Matlab程式是解釋執行所帶來效率問題,我們應該儘量使用“向量化”(vectorization)的命令。Matlab程式執行的效率,對於是否使用了“向量化”命令是非常(very)敏感的!

  其後,文章給出了兩條實用的建議。

  第一條,使用向量操作代替迴圈。 以下舉例說明。

1 dx = pi/30;
2 nx =1+2*pi/dx;
3 for i =1:nx
4   x(i) = (i-1)*dx;
5   y(i) =sin(3*x(i));
6 end

  這段程式碼是很自然的從C語言的形式轉化而來的,但其效率很低!Matlab是實時為變數分配記憶體的,在第一遍迴圈時(即i=1時),Matlab為x和y這兩個向量(長度均為1)分配記憶體。以後每執行一次迴圈,Matlab都會在x和y的末尾附加新的元素。這不僅導致分配記憶體的呼叫的增加,也使得x和y的各個元素在記憶體中的分佈不是連續的(就像資料結構中陣列和連結串列的區別)。由此,效能遭到了損失。

  相比之下,下面的程式碼效率提高不少:

1 =0:pi/30:2*pi
2 =sin(3*x);

  第一個語句分配了一個連續的記憶體空間來儲存具有多個元素的向量x。類似的,第二個語句在分配記憶體時,也是分配了一個連續的記憶體空間來儲存具有多個元素的向量y。撇去計算sin的消耗不算,就記憶體分配命令的執行次數和對向量元素訪問的方便程度來說,高下立見。

  第二條,為矩陣和向量預先分配記憶體。

  文章中指出,雖然Matlab會自動調整變數的大小,我們最好還是預先為變數分配記憶體空間。因為這樣可以使呼叫記憶體分配命令的次數降為1,也可以使變數在記憶體中連續儲存(當變數為矩陣時是按列在記憶體中連續儲存)。

  而所謂“預先為變數分配記憶體空間” ,是指在知道變數的大小的情況下,在變數中的任何一個元素都未被引用之前,建立一個大小和其一致的變數。

  下面是一個例子,程式碼質量從上至下逐漸提高: 

複製程式碼  1 dx = pi/30;
 2 nx =1+2*pi/dx;
 3 nx2 = nx/2;
 4  5 for i =1:nx2
 6   x(i) = (i-1)*dx;
 7   y(i) =sin(3*x(i));
 8 end 9 10 for i = nx2+1:nx
11   x(i) = (i-1)*dx;
12   y(i) =sin(5*x(i));
13 end 複製程式碼 複製程式碼  1 dx = pi/30;
 2 nx =1+2*pi/dx;
 3 nx2 = nx/2;
 4  5 = zeros(1,nx);      % 為向量x預分配記憶體
 6 = zeros(1,nx);      % 為向量y預分配記憶體
 7  8 for i =1:nx2
 9   x(i) = (i-1)*dx;
10   y(i) =sin(3*x(i));
11 end12 13 for i = nx2+1:nx
14   x(i) = (i-1)*dx;
15   y(i) =sin(5*x(i));
16 end 複製程式碼 複製程式碼  1 =0:pi/30:2*pi;     % 計算向量x的值
 2 nx = length(x);
 3 nx2 = nx/2;
 4  5 = x;                % 為向量y預分配記憶體
 6  7 for i =1:nx2
 8   y(i) =sin(3*x(i));
 9 end10 11 for i = nx2+1:nx
12   y(i) =sin(5*x(i));
13 end 複製程式碼 複製程式碼 1 =0:pi/30:2*pi;                  % 計算向量x的值
2 nx = length(x);
3 nx2 = nx/2;
4 5 = x;                             % 為向量y預分配記憶體
6 7 y(1:nx2) =sin(3*x(1:nx2));        % 計算y的第1部分的值
8 y(nx2+1:nx) =sin(5*x(nx2+1:nx));  % 計算y的第2部分的值 複製程式碼

  除了上篇文章提到的那幾點以外,該ppt中還提出了以下幾點看法和建議。

  第一,選擇合適的資料型別。Matlab有多種資料型別,不同的資料型別可以帶來不同的精度,但處理速度也存在差別。double當然可以比int8帶來更高的精度,但效能卻會下降。不過,我個人對這個建議持保留意見,主要在於有些操作對一些諸如int8型別的非標準型別不支援,而且有時候容易產生誤操作(例如相對uint8這樣的無符號整形變數)。

  第二,使用tic和toc來測試程式的執行時間。這兩個命令配合使用可以測試一段m程式碼的執行時間。具體的就不多說了,大家可以去檢視Matlab的幫助檔案。另外,Matlab最近的版本(像R2009b)中出現了類似於效能測試工具的元件,大家可以在Matlab的幫助檔案中搜索"Profiling for Improving Performance" 進行進一步瞭解。

  第三,類似於上一篇文章中提到的使用向量化命令,減少迴圈。但是,該ppt中還列出了一些常用的可以用來代替迴圈的向量化命令,列舉如下:

  • find        (find values that meet some criteria,尋找符合某些特定條件的矩陣中的元素)
  • sum, prod, diff   (sum 加, product 乘, difference 減)
  • .*, ./        (element by element matrix operations,矩陣間逐元素操作)
  • min, max       (find min or max values,求最小和最大值)
  • zeros, ones     (for initializing arrays,用於初始化變數)

  其中,我覺得find、prod、diff等都是比較少見的(可能由於我才疏學淺,呵呵),大家可以仔細研究一下。尤其是find,非常有用!

  總的來說,就是對for迴圈不能一棒子打死,要區別對待(像對goto語句?)。

  在這個帖子中,名為Bruno Luong的作者總結道(本人英文不好,不敢打包票翻譯對了,故附上原文~~~):

  • if there is an equivalent vectorized stock function, always use it(如果有等價的向量化命令,毫不猶豫的使用後者)
  • avoid for-loop that call function with non negligible overhead(避免在for迴圈中呼叫計算量很大的函式)
  • for loop is desirable when a nested IF condition can be used to save computation time(如果for迴圈中有if語句,並可以因此而帶來時間的節省,那麼for語句是值得試試的)
  • for loop is attractive when the result of the preceding iteration(s) can be used to save computation effort of the current calculation(如果上一次的迴圈得出的結果對本次迴圈有幫助,可以節省計算量,那麼for迴圈是比較吸引人的)
  • using for loop is not recommended when the large data need to be duplicated inside the loop(當迴圈中存在大量的資料複製時,for迴圈是不值得推薦使用的)
  • time it, time it and time it(不停的測試,測試不用for和用for的區別,“唯利是圖”就可以了)
  • Read Matt Fig's post!(閱讀Matt Fig的帖子!【Matt Fig是哪位大神?我沒Google,大家看看他發表了什麼高見,然後告訴我一聲啊~】)

相關推薦

衛語句,多層迴圈優化

if(!CollectionUtils.isEmpty(list)){ for(Map<String,Object>map :list){

雙重大陣列迴圈優化

雙重大陣列迴圈優化 一、前言 這幾天發現服務在凌晨時容易報警,持續半個小時才正常,第二天分析日誌和檢查程式碼發現,有一個過濾黑白名單的操作,其中黑名單的資料有39萬,白名單資料30萬,然後處理的資料也有80萬左右,在業務邏輯中黑白名單本身有一個過濾邏輯,資料對黑白名單有一個過濾邏

java之list迴圈優化(一)

需要將for (int i = 0; i < list.size(); i++)改為int j = list.size(); for (int i = 0; i < j; i++)嗎?

for迴圈優化總結

因為最近專案中大量使用到for迴圈,所以稍微總結了一下 1:多個for迴圈時,遵循外小內大(從外至裡,迴圈物件size要從小到大) 2:提取與迴圈無關表示式到迴圈外 3:消除迴圈終止判斷,

Matlab的for迴圈優化

因為學習和工作的原因,最近又開始使用已經許久沒有接觸的Matlab。在沒有什麼特殊考慮的情況下,信手寫下了下面的m程式碼片段:  1 for i=1:1:(imgHeight-tmpHeight+1) 2 for j=1:1:(imgWidth-tmpWidth+

MATLAB中對矩陣元素操作的for迴圈優化方法

           眾所周知,MATLAB程式效率最低最有潛力的地方便是迴圈了。最常見的迴圈莫過於對矩陣中的每一個元素進行操作,對於程式設計思維還在C語言或者C++,JAVA的人來說,第一反應就是兩層迴圈,先來個 “for i=1:m”對矩陣的行進行迴圈,再來個“for

JS之for迴圈優化

眾所周知Js中的迴圈大致可以分為4中: 1.for迴圈  Javascript程式碼   for(var i=0;i<10;i++){     //迴圈主題   }   其中for迴圈是最常見的迴圈結構,由四部分組成:初始化、前測條件、後執行體、迴圈體。當代碼執

菜鳥要做架構師——java效能優化之for迴圈

完成同樣的功能,用不同的程式碼來實現,效能上可能會有比較大的差別,所以對於一些效能敏感的模組來說,對程式碼進行一定的優化還是很有必要的。今天就來說一下java程式碼優化的事情,今天主要聊一下對於for(while等同理)迴圈的優化。 作為三大結構之一的迴圈,在我們編寫程式碼的時候會經常用到。

python 優化迴圈

for迴圈 import numpy as np from datetime import datetime img=np.random.random([10000,10000]) start_time=datetime.now() img2=[] for i in img: for

初夏小談:旋轉字串優化1.0,2.0(不用迴圈

左旋與右旋原理一樣。之前旋轉不夠簡單,對此研究出更加優化的演算法。 #include<Aventador_SQ.h> //優化1.0 void XuanZhuan1(char *arr, int k) { char arr1[1024] = "0"; int i = 0; i

二叉搜尋樹與雙向連結串列的優化,設定全域性變數指向最後一次遍歷的從而連線,省略了迴圈找到最後的節點進行連線

package niuke; public class SearchTreenode3 {     TreeNode lastNode = null;       public static void main(String[] arg

優化SQL Server迴圈更新、插入耗時長的問題】

一: 工作當中遇到更新較多資料時,使用迴圈(while,或遊標)進行增刪改時,特別費時  WHILE @i <= @rowsBEGIN   SELECT @appNo = AppNumber, @roleid = RoleId, @statusi= Status, @empId = Empl

java8的新特性之List集合雙層for迴圈效率優化

首先先來一個案例簡單模擬一下: List<String> wifes = new ArrayList<String>(); List<String> husbands= new ArrayList<String>(); for(int i=0;i&

######業務儘可能用sql實現(縮短後臺響應時間):最近都在優化之前的程式碼:發現每個模組的index頁都需要優化(很多都在迴圈裡操作資料庫:菜!初級!)

  ###最近都在優化之前的程式碼: 發現每個模組的index頁都需要優化(很多都再迴圈裡操作資料庫:菜!初級!或者說趕專案沒想優化這回事吧) ===》解耦合前提下,能用一條SQL解決的,多測試寫SQL。可以減少很多程式碼。 希望我在疲憊,腦子不好使時候也能堅持吧。(注意休

Java效能優化--for迴圈

最近在學習關於Java效能優化及JVM的一些知識,無意中想起for迴圈的應用,畢竟太多的演算法題和實際開發都會用到它,也就萌生了對它進行優化的想法。 不過這裡要提出的是,同一段程式碼在不同機器和不同版本jdk中執行可能是會出現很大差別的,我用的jdk版本:1.8.0_1

JavaScript——for和for in 的效能比較與for迴圈優化方案

在JavaScript中,我們遍歷陣列的時候經常需要用到for和for in。今天來比較一下這兩個遍歷方法的效能,並提供優化方案。 1.for 和for in的效能比較 我們都知道,for 和for in的時間複雜度一樣,但是其效能有些許差距。具體有多大差距呢,下面我們來

8皇后以及N皇后演算法探究,回溯演算法的JAVA實現,非遞迴,迴圈控制及其優化

研究了遞迴方法實現回溯,解決N皇后問題,下面我們來探討一下非遞迴方案 實驗結果令人還是有些失望,原來非遞迴方案的效能並不比遞迴方案效能高 程式碼如下: package com.newflypig.eightqueen; import java.util.Date; /**

巢狀For迴圈效能優化分析

1、案例描述 某日,在JavaEye上看到一道面試題,題目是這樣的:請對以下的程式碼進行優化   [java] view plain copy print? for (int i = 0;&n

迴圈查詢資料的效能問題及優化

寫在前言:糟糕的程式碼,對程式碼維護、效能、團隊協作都會造成負面影響,所以,先設計再實現,謀而後動。         這裡的迴圈查詢,指的是在一個for迴圈中,不斷訪問資料庫來查詢資料。在剛接手公司

論使用HashMap優化雙層For迴圈的實際效能

當需要對兩個集合進行相互操作的時候,一般需要進行雙層For迴圈,但我們知道雙層For在數量越大的時候效能影響越大 這時候我們會想到的其中一種解決方法就是利用Hashmap在查詢資料的高效上來優化雙層For 我利用下面的程式碼來模擬測試兩種情況的效能: public static void main