好文轉貼自
關 於matlab的效率問題,很多文章,包括我之前寫的一些,主要集中在使用向量化以及相關的問題上。但是,最近我在實驗時對**進行profile的過程 中,發現在新版本的matlab下,for-loop已經得到了極大優化,而效率的瓶頸更多是在函式呼叫和索引訪問的過程中。
由於matlab特有的解釋過程,不同方式的函式呼叫和元素索引,其效率差別巨大。不恰當的使用方式可能在本來不起眼的地方帶來嚴重的開銷,甚至可能使你的**的執行時間增加上千倍(這就不是多買幾台伺服器或者增加計算節點能解決的了,呵呵)。
下 面通過一些簡單例子說明問題。(實驗選在裝有windows vista的一台普通的台式電腦(core2 duo 2.33ghz + 4gb ram)進行,相比於計算集群, 這可能和大部分朋友的環境更相似一些。實驗過程是對某乙個過程實施多次的整體進行計時,然後得到每次過程的平均時間,以減少計時誤差帶來的影響。多次實 驗,在均值附近正負20%的範圍內的置信度高於95%。為了避免算上首次執行時花在預編譯上的時間,在開始計時前都進行充分的「熱身」執行。)
函式呼叫的效率
乙個非常簡單的例子,把向量中每個元素加1。(當然這個例子根本不需要調函式,但是,用它主要是為了減少函式執行本身的時間,突出函式解析和呼叫的過程。)
作為baseline,先看看最直接的實現
% input u: u is a 1000000 x 1 vector這個過程平均需要0.00105 sec。而使用長期被要求盡量避免的for-loopv = u + 1;
n = numel(u);所需的平均時間大概是0.00110 sec。從統計意義上說,和vectorization已經沒有顯著差別。無論是for-loop或者vectorization,每秒平均進行約10億次「索引-讀取-加法-寫入」的過程,計算資源應該得到了比較充分的利用。% v = zeros(n, 1) has been pre-allocated.
for i = 1 : n
v(i) = u(i) + 1;
end
要是這個過程使用了函式呼叫呢?matlab裡面支援很多種函式呼叫方式,主要的有m-function, function handle, anonymous function, inline, 和feval,而feval的主引數可以是字串名字,function handle, anonymous function或者inline。
用m-function,就是專門定義乙個函式
function y = fm(x)在呼叫時y = x + 1;
for i = 1 : nfunction handle就是用@來把乙個function賦到乙個變數上,類似於c/c++的函式指標,或者c#裡面的delegate的作用v(i) = fm(u(i));
end
fh = @fm;anonymous function是一種便捷的語法來構造簡單的函式,類似於lisp, python的lambda表示式for i = 1 : n
v(i) = fh(u(i));
end
fa = @(x) x + 1;inline function是一種傳統的通過表示式字串建構函式的過程for i = 1 : n
v(i) = fa(u(i));
end
fi = inline('x + 1', 'x');feval的好處在於可以以字串方式指定名字來呼叫函式,當然它也可以接受別的引數。for i = 1 : n
v(i) = fi(u(i));
end
v(i) = feval('fm', u(i));對於100萬次呼叫(包含for-loop本身的開銷,函式解析(resolution),壓棧,執行加法,退棧,把返回值賦給接收變數),不同的方式的時間差別很大:v(i) = feval(fh, u(i));
v(i) = feval(fa, u(i));
m-function
0.385 sec
function handle
0.615 sec
anonymous function
0.635 sec
inline function
166.00 sec
feval('fm', u(i))
8.328 sec
feval(fh, u(i))
0.618 sec
feval(fa, u(i))
0.652 sec
feval(@fm, u(i))
2.788 sec
feval(@fa, u(i))
34.689 sec
從這裡面,我們可以看到幾個有意思的現象:
在2023年以後,matlab推出了arrayfun函式,上面的for-loop可以寫為
v = arrayfun(fh, u)這平均需要4.48 sec,這比起for-loop(需時0.615 sec)還慢了7倍多。這個看上去「消除了for-loop"的函式,由於其內部設計的原因,未必能帶來效率上的正效果。
元素和域的訪問
除了函式呼叫,資料的訪問方式對於效率也有很大影響。matlab主要支援下面一些形式的訪問:
這裡主要探索單個元素或者域的訪問(當然,matlab也支援對於子陣列的非常靈活整體索引)。
對於一百萬次訪問的平均時間
a(i) for a numeric array
0.0052 sec
c for a cell array
0.2568 sec
struct field
0.0045 sec
struct field (with dynamic name)
1.0394 sec
我們可以看到matlab對於單個陣列元素或者靜態的struct field的訪問,可以達到不錯的速度,在主流台式電腦約每秒2億次(連同for-loop的開銷)。而cell array的訪問則明顯緩慢,約每秒400萬次(慢了50倍)。matlab還支援靈活的使用字串來指定要訪問域的語法(動態名字),但是,是以巨大的 開銷為代價的,比起靜態的訪問慢了200倍以上。
關於object-oriented programming
matlab在新的版本中(尤其是2008版),對於物件導向的程式設計提供了強大的支援。在2008a中,它對於oo的支援已經不亞於 python等的高階指令碼語言。但是,我在實驗中看到,雖然在語法上提供了全面的支援,但是matlab裡面物件導向的效率很低,開銷巨大。這裡僅舉幾個 例子。
建議 根據上面的分析,對於撰寫高效matlab**,我有下面一些建議:
雖然for-loop的速度有了很大改善,vectorization(向量化)仍舊是改善效率的重要途徑,尤其是在能把運算改寫成矩陣乘法的情況下,改善尤為顯著。
在不少情況下,for-loop本身已經不構成太大問題,尤其是當迴圈體本身需要較多的計算的時候。這個時候,改善概率的關鍵在於改善迴圈體本身而不是去掉for-loop。
matlab的函式呼叫過程(非built-in function)有顯著開銷,因此,在效率要求較高的**中,應該盡可能採用扁平的呼叫結構,也就是在保持**清晰和可維護的情況下,盡量直接寫表示式 和利用built-in function,避免不必要的自定義函式呼叫過程。在次數很多的迴圈體內(包括在cellfun, arrayfun等實際上蘊含迴圈的函式)形成長呼叫鏈,會帶來很大的開銷。
在呼叫函式時,首選built-in function,然後是普通的m-file函式,然後才是function handle或者anonymous function。在使用function handle或者anonymous function作為引數傳遞時,如果該函式被呼叫多次,最好先用乙個變數接住,再傳入該變數。這樣,可以有效避免重複的解析過程。
在可能的情況下,使用numeric array或者struct array,它們的效率大幅度高於cell array(幾十倍甚至更多)。對於struct,盡可能使用普通的域(字段,field)訪問方式,在非效率關鍵,執行次數較少,而靈活性要求較高的代 碼中,可以考慮使用動態名稱的域訪問。
雖然object-oriented從軟體工程的角度更為優勝,而且object的使用很多時候很方便,但是matlab目前對於oo的實現效率很低,在效率關鍵的**中應該慎用objects。
如果需要設計類,應該盡可能採用普通的property,而避免靈活但是效率很低的dependent property。如非確實必要,避免過載subsref和subsasgn函式,因為這會全面接管對於object的介面呼叫,往往會帶來非常巨大的開 銷(成千上萬倍的減慢),甚至使得本來幾乎不是問題的**成為效能瓶頸。
Matlab撿知識 S Function再體驗
作為乙個學控制 越來越偏離軌道 的學生,使用simulink的小部分功能是在頻繁不過的了,但是我本科到碩士一年級,用simulink呼叫自建函式,不是簡單s函式就是複雜m function,基本都會避開s function 一開始感覺賊麻煩 其實,m function和s function同時用到複...
Matlab向量化再探
之前的觀點是matlab的向量化的計算效率比for迴圈高。今天考察乙個例子,計算10000個隨機數相加,平台是matlab 2018b。先說結論 進行預定義矩陣尺寸的for迴圈並且減少迴圈中的重複計算的情況下,效率比自帶函式的效率要高。從此看出,提高效率的關鍵在於定義矩陣尺寸,而向量化不能有效的提高...
MATLAB入門(MATLAB命令)
管理會話中的命令 clc 刪除命令視窗 clear 刪除記憶體中的變數 exist 檢查變數是否存在 global 宣告變數為全域性變數 help 獲取幫助資訊 1 查詢各種算術運算子 關係運算子 邏輯運算子 2 查詢名稱準確已知的命令或檔案 3 非matlab自帶.m檔案的幫助性注釋內容 look...