新聞中心
MATLAB語言是一種被稱為是“演算紙”式的語言,因此追求的是方便性、靈活性以及交互性。在快速性上要比C語言這種性能強(qiáng)勁著稱的稍遜一籌。然而,通過一些手段,我們也能讓MATLAB語言快起來,甚至和C差不多了!

成都創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的圖們網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
首先聲明:本文是一個(gè)初級(jí)教程,因此很多知識(shí)是假定你已經(jīng)很熟悉了的;雖然我在討論讓代碼飛起來,但從來不會(huì)說最快有多快,究竟有多快你要自己感覺;作者水平不是很高,難免誤導(dǎo)你,小心甄別。
在正式討論之前,先看看這些好習(xí)慣你有沒有?
1. 使用 M-Lint
M-Lint是一個(gè)代碼分析檢查工具,它在你寫代碼的過程中實(shí)時(shí)交互,發(fā)現(xiàn)你代碼的問題,按照最佳性能和最可維護(hù)性給出修改建議。
注意:我可沒說是最正確!
如果沒有激活這個(gè)功能,依次使用File > Preferences > M-Lint,勾選Enable integrated M-Lint warning and error messages 。同時(shí),還可以設(shè)定你的偏好。
激活后,在你寫代碼時(shí)就會(huì)實(shí)時(shí)交互了,錯(cuò)誤的或者不推薦的部分會(huì)以紅色下劃線標(biāo)出,鼠標(biāo)經(jīng)過紅色下劃線的語句或單詞,M-Lint給出提示信息。想一下子看遍全部提示信息。使用Tools >M-Lint > (Save and) Show M-Lint Report2。
注:首次“觀看”先提示先保存一下。
2. 組織
給每一個(gè)項(xiàng)目(project)建立一個(gè)單獨(dú)的文件夾。同屬于一個(gè)項(xiàng)目的文件保存在哪兒的都有,你找的時(shí)候就不費(fèi)勁嗎!
寫頭部注釋,尤其是H1。第一行就是H1。MATLAB中的內(nèi)置函數(shù)的 help的內(nèi)容其實(shí)就是讀取的這個(gè)函數(shù)的頭部注釋。怎么寫,參照MATLAB內(nèi)置函數(shù)。
將經(jīng)常用到的控制臺(tái)命令存儲(chǔ)為腳本(script)。如果有些命令反復(fù)使用,還是存為腳本吧,沒別的意思,你要少敲多少次鍵盤啊!
3. 避免數(shù)據(jù)丟失
不要在腳本中使用 clear all。不幸的是這是一個(gè)大家常用的命令,有些書上還作為一條規(guī)則確立起來,建議必須使用!要知道這個(gè)命令一執(zhí)行,工作空間的數(shù)據(jù)可就不可逆轉(zhuǎn)的全沒了啊!
警告:注意呦! ?
小心同名覆蓋。如果你在一個(gè)文件中,本來你的意思是兩個(gè)變量,你卻給他們起了相同的名字,那么第一次的數(shù)據(jù)可就沒了。比如:
- result=max(a,b); %想求 a和 b之較大者
- result=max(c,d); %想求 c 和d之較大者
result結(jié)果是什么?恐怕不是你想要的。不妨將其改為result1和 result2。類似的,也要小心文件重名的覆蓋,這個(gè)后果貌似更嚴(yán)重些。
下條內(nèi)容請(qǐng)重視!
如何讓 MATLAB崩潰。
盡管 MATLAB是很穩(wěn)定的,但是我們?nèi)匀豢梢宰屗罎?使用第三方的MEX函數(shù)或者耗內(nèi)存的操作比如視頻處理或者超大規(guī)模矩陣都可能會(huì)造成MATLAB崩潰。
#p#
如果你已經(jīng)有這些好習(xí)慣,那么恭喜,你要是還有其他好習(xí)慣麻煩也告訴我一聲!如果沒有,相信你看完之后總該有了吧?好了,我們開始!
1.使用profile
profile,Longman 給出的解釋是:a short description that gives important details about a person, a group of people, or a place。
MATLAB中內(nèi)置了一個(gè)叫做profile的工具,來協(xié)助評(píng)估程序,也就是對(duì)程序運(yùn)行過程的一個(gè)short description吧。主要命令有:
profile on 開啟
profile off 關(guān)閉
profile clear 清空數(shù)據(jù)
profile viewer 在profiler中看結(jié)果
下面我們?cè)u(píng)估一下下面這個(gè)函數(shù):
- function result =example1(count)
- for k = 1:count
- result(k) = sin(k/50);
- if result(k)<-0.9
- result(k) = gammaln(k);
- end
- end
為了分析這個(gè)函數(shù)的效率,首先開啟并清空 profiler,然后運(yùn)行這個(gè)函數(shù),接下來看結(jié)果報(bào)告。即依次輸入:
- >> profile on, profile clear
- >> example1(5000);
- >> profile viewer
這就是 profile 的基本語法。也有使用鼠標(biāo)操作的方法,這里就不介紹了,那樣雖然直觀單遠(yuǎn)不及使用,命令方便。
由于系統(tǒng)的不同,報(bào)告的結(jié)果一般是不一樣的。以下是我的系統(tǒng)得出的結(jié)果。
1.先看profile summary:
2.點(diǎn)擊example1鏈接,進(jìn)入具體各小項(xiàng)的評(píng)估。
?。?)調(diào)用函數(shù)(children)、被調(diào)用函數(shù)(parents)。本例中都沒有。如果被 profile 的對(duì)象有調(diào)用函數(shù)或者被調(diào)用函數(shù)的話,會(huì)給出相應(yīng)的數(shù)據(jù)。
?。?)時(shí)間在哪些行被消耗(Lines where the most time was spent):
從數(shù)據(jù)中我們可以看出哪些行消耗了多少時(shí)間(總時(shí)間和相對(duì)時(shí)間),被調(diào)用了多少次,以及直觀的柱形圖。
?。?)另一個(gè)有用的項(xiàng)目是 M-Lint 結(jié)果,給出了錯(cuò)誤(警告、提示)所在的行,以及對(duì)應(yīng)的建議修改信息,這些建議對(duì)代碼的改進(jìn)是很有價(jià)值的信息:
?。?)最下面還有一個(gè)函數(shù)列表,是(2)的另一種形式??磮D:
最右側(cè)是函數(shù)代碼,前有行號(hào)、每一行調(diào)用的次數(shù)和小號(hào)的時(shí)間。消耗時(shí)間最多的行被標(biāo)示了出來。最紅的消耗時(shí)間最多。
profiler工具的時(shí)間分辨率不是很高,因此,如果你的代碼運(yùn)行的時(shí)間很短,有時(shí)候恐怕不能感知到。這時(shí)候不妨人為的加入幾個(gè)循環(huán),讓程序所運(yùn)行幾次,然后進(jìn)行分析。
必須指出,profile工具的作用主要是分析程序,獲得程序運(yùn)行的信息。如果想要知道程序運(yùn)行的精確時(shí)間,使用計(jì)時(shí)器 tic/toc。以上面程序?yàn)槔?在命令行輸入:
- >> tic;example1(5000);toc
輸出是:
- Elapsed time is 0.058522 seconds.
為了獲得更為精準(zhǔn)的結(jié)果,你最好把瀏覽器、殺毒軟件、防火墻等等占用CPU時(shí)間片的程序先關(guān)了,只剩下不能關(guān)掉的系統(tǒng)進(jìn)程。
注意:profile在新版本中不斷被加強(qiáng),可使用的參數(shù)也越來越多,不過大多數(shù)根本用不著,如果你覺得那些參數(shù)很有用,我相信你根本用不找看我這個(gè)小冊(cè)子了,要真是這樣,麻煩您不吝賜教,分享一些經(jīng)驗(yàn)。更詳細(xì)的內(nèi)容,您還是去看文檔去吧!
#p#
2. 預(yù)分配矩陣
MATLAB中的矩陣變量可以動(dòng)態(tài)增長行和列。比如:
- >>x=2
- x=
- 2
- >>x(2,3)=1
- x=
- 2 0 0
- 0 0 1
看到?jīng)]?MATLAB自動(dòng)調(diào)整了矩陣的大小!從內(nèi)部實(shí)現(xiàn)上看,矩陣數(shù)據(jù)存儲(chǔ)單元被重新分配了更大的單元。如果矩陣的大小被反復(fù)的調(diào)整(比如在循環(huán)中),重新分配存儲(chǔ)空間帶來的額外開銷會(huì)是很顯著的。為了避免反復(fù)的矩陣存儲(chǔ)重新分配,預(yù)分配矩陣的存儲(chǔ)單元是一個(gè)不錯(cuò)的選擇。一個(gè)推薦的方法是使用 zeros 函數(shù)命令。看下面的代碼:
- a(1) = 1;
- b(1) = 0;
- for k = 2:8000
- a(k) = 0.99803 * a(k-1)-0.06279 * b(k-1);
- b(k) = 0.06279 * a(k-1) + 0.99803 * b(k-1);
- end
- tic/toc計(jì)時(shí)運(yùn)行得到:
- Elapsed time is 0.013306 seconds.
簡(jiǎn)單分析上面的代碼,知道,每一次 for,矩陣 a 和 b 的大小都要被重新分配,最終的大小事 8000 的列向量。如果我們提前就給它們分配好大小為 8000的存儲(chǔ)空間,看看結(jié)果怎么樣:
- a=zeros(1,8000); %預(yù)分配矩陣存儲(chǔ)單元
- b=zeros(1,8000);
- a(1) = 1;
- b(1) = 0;
- for k = 2:8000
- a(k) = 0.99803 * a(k-1)-0.06279 * b(k-1);
- b(k) = 0.06279 * a(k-1) + 0.99803 * b(k-1);
- end
- 及時(shí)運(yùn)行得到:
- Elapsed time is 0.000753 seconds.
看出來沒?速度提高了近 18 倍!像這種只需添加幾行代碼就能做到的情況是很多的。這個(gè)例子也有特殊性,就是最后的結(jié)果大小已知,如果結(jié)果的大小可變、未知呢?沒關(guān)系,我們可以估計(jì)一下,最終結(jié)果最大能是多少?比估計(jì)到的最大再留出一些余量就成了!如果你估計(jì)的還是不夠大,那超出的部分還要反復(fù)重新分配,不過這樣節(jié)省下來的時(shí)間也是很可觀的,畢竟可以少分配很多次了! 最后呢,還要處理一下后事,比如你分配給變量 a 有 1000 個(gè)單元,但最終它只占了300個(gè),那你還要將那700個(gè)給收回來??聪旅娴拇a:
- a = zeros(1,10000); % 預(yù)分配
- count = 0;
- for k = 1:10000
- v = exp(rand*rand);
- if v > 0.5 % 增長結(jié)果不確定的來源
- count = count + 1; a(count) = v;
- end
- end
- a = a(1:count); %調(diào)整矩陣大小
- 未預(yù)分配時(shí):Elapsed time is 0.052395 seconds.
- 預(yù)分配后:Elapsed time is 0.008935 seconds.
感慨:些微時(shí)間的意義在哪呢?背后是你對(duì) MATLAB 的理解深度。哥玩的不是時(shí)間,是技術(shù)。
#p#
3. 向量化
很多情況下,程序中的某些代碼可以被向量化,向量化前后的速度往往在10 倍以上!向量化是最基本和最有效的讓代碼快起來的技巧,我都不愿意在后面叫“之一”了。
?。?)向量化的計(jì)算
很多常規(guī)函數(shù)都是向量化的,它們作用于數(shù)組時(shí),就好像是作用于數(shù)組中的每一個(gè)元素。例如:
- >> sqrt([1,4,9,16])
- ans =
- 1 2 3 4
- 考慮下面的函數(shù):
- function d = minDistance(x,y,z) %尋找點(diǎn)集中距離遠(yuǎn)點(diǎn)最近點(diǎn)
- nPoints = length(x);
- d = zeros(nPoints,1); % 預(yù)分配
- for k = 1:nPoints % 計(jì)算每一個(gè)點(diǎn)的距離
- d(k) = sqrt(x(k)^2 + y(k)^2 + z(k)^2);
- end
- d = min(d); % 得到最小距離
- 取 x=[1 2 3 4 5 6]; y=[2 3 5 2 1 4];z=[9 2 3 2 1 5];
- 計(jì)時(shí)運(yùn)行:Elapsed time is 0.008006 seconds.
如果你寫出上面類似的代碼,說明你認(rèn)真看了前面的內(nèi)容。為d預(yù)分配空間確實(shí)為本例節(jié)省了不少時(shí)間。如果采用向量化計(jì)算,我們可以去掉for循環(huán),直接計(jì)算向量。這里要隆重推出“.”運(yùn)算符,它表示的是對(duì)應(yīng)元素進(jìn)行運(yùn)算。有.*和./和.\和.'和.^等。分別表示不帶.運(yùn)算的對(duì)應(yīng)元素運(yùn)算。假設(shè)A是方陣,A^2是矩陣的 2 次乘冪,而 A.^2 表示矩陣 A 中的元素各自求平方組成新的矩陣??紤]下面的代碼:
- function d = minDistance(x,y,z)
- d = sqrt(x.^2 + y.^2 + z.^2); % 計(jì)算每一點(diǎn)的距離
- d = min(d);
- 計(jì)時(shí)運(yùn)行:
- Elapsed time is 0.005326 seconds.
貌似差別不大?這就對(duì)了,別忘了,咱可就計(jì)算了6個(gè)值啊!這么幾個(gè)值就有了這樣的差距,那x、y、z向量要是大一點(diǎn),結(jié)果的差異就可想而知了!
更進(jìn)一步的,我們可以使用d = sqrt(min(x.^2 + y.^2 + z.^2))取代后兩行語句,讓程序更加簡(jiǎn)潔。
一下函數(shù)使用向量化的計(jì)算會(huì)更為節(jié)省時(shí)間:min, max, repmat, meshgrid,sum, diff, prod等等。
?。?)向量化邏輯
上面討論了計(jì)算的向量化,其實(shí)MATLAB的邏輯運(yùn)算也是向量化的。比如:
- >> [1 4 2]>[2 3 1]
- ans =
- 0 1 1
兩個(gè)數(shù)組“按元素”進(jìn)行比較。向量的邏輯操作返回二進(jìn)制的邏輯結(jié)果向量,即用0代表假,用1代表真。這為什么有用呢?因?yàn)镸ATLAB中有一些強(qiáng)勁的針對(duì)邏輯向量的函數(shù)。例如:
- >> find([1,5,3] < [2,2,4])
- ans =
- 1 3
- >> any([1,5,3] < [2,2,4])
- ans =
- 1
- >> all([1,5,3] < [2,2,4])
- ans =
- 0
其實(shí),對(duì)一般向量(非邏輯向量)也是適用的!
- >> find(eye(4)==1)
- ans =
- 1
- 6
- 11
- 16
以上函數(shù)的用法請(qǐng)自己查閱函數(shù)說明。
#p#
4. 示例
?。?)向量歸一標(biāo)準(zhǔn)化
將一個(gè)向量v歸一標(biāo)準(zhǔn)化,我們可是使用v = v/norm(v),norm函數(shù)的作用是求模(范數(shù))。
如果對(duì)一組向量 v(:,1), v(:,2),…進(jìn)行歸一標(biāo)準(zhǔn)化,可以使用一個(gè)循環(huán)計(jì)算v(:,k)/norm(v(:,k))。更好的策略是向量化計(jì)算:
- vMag = sqrt(sum(v.?2));
- v = v./vMag(ones(1,size(v,1)),:);
?。?)剔除元素
有時(shí)候,我們需要將矩陣中的符合某些條件的元素剔除,當(dāng)然可以使用條件判斷加循環(huán)。我們使用向量化剔除矩陣中的NaN和無窮兩類數(shù):
- i = find(isnan(x) | isinf(x)); %在x中找到符合條件的數(shù)的位置
- x(i) = []; %剔除它
- 或者,同樣的功能:
- i = find(?isnan(x) & ?isinf(x)); %找到不符合的數(shù)
- x = x(i); %保留它
- 進(jìn)一步的,我們可以更加簡(jiǎn)化,省略中間變量:
- x(isnan(x) | isinf(x)) = [];
- 以及
- x = x(?isnan(x) & ?isinf(x));
(3)分段函數(shù)
信號(hào)分析中十分重要的 sinc(x)函數(shù)是分段的:x=0 時(shí)的值是 1,x!=0 時(shí),sinc(x)=sin(x)/x。下面的代碼使用向量化方法處理分段:
- function y = sinc(x)
- y = ones(size(x)); % 先設(shè)所有的y都是1
- i = find(x ?= 0); % 找到非零x值
- y(i) = sin(x(i)) ./ x(i); % 計(jì)算非零值處的函數(shù)值
- 更簡(jiǎn)潔的,可以寫成:
- y = (sin(x) + (x == 0))./(x + (x == 0))
能看出來嗎?里面用到了邏輯運(yùn)算,實(shí)在是巧妙的很!
?。?)其他
還有些不常用的,算了,知道也八輩子用不著,珍惜腦細(xì)胞吧!
感慨:向量、矢量、相量、復(fù)數(shù)、數(shù)組、矩陣,這些名詞能分清楚么?能分清楚知道內(nèi)涵也就是為什么要這樣規(guī)定么?不會(huì)也別問我!
#p#
5. 內(nèi)嵌簡(jiǎn)單函數(shù)
內(nèi)嵌函數(shù)的意思就是將函數(shù)調(diào)用的函數(shù)的代碼直接寫到這個(gè)函數(shù)里面來。由于函數(shù)調(diào)用要做保護(hù)現(xiàn)場(chǎng)以及恢復(fù)現(xiàn)場(chǎng)等工作,也會(huì)額外增加一些時(shí)間消耗。如果調(diào)用的次數(shù)不是很多,這些時(shí)間是可以忽略的,但是當(dāng)調(diào)用次數(shù)很多的時(shí)候(比如500次),這個(gè)時(shí)間就很可觀了!
什么樣的被調(diào)用函數(shù)適合內(nèi)嵌呢?正如標(biāo)題所說,是簡(jiǎn)單的函數(shù),特征呢就是這個(gè)函數(shù)只有幾行代碼。如果這個(gè)函數(shù)很復(fù)雜,代碼很長,還是死了這個(gè)心吧,內(nèi)嵌是內(nèi)嵌了,可是你看不懂代碼了,得不償失。程序的可讀性是非常重要的!
注意:必須是 M-File 實(shí)現(xiàn)的函數(shù)才能內(nèi)嵌!
下面的代碼演示一個(gè)反復(fù)調(diào)用median函數(shù)的內(nèi)嵌方法。原代碼:
- y = zeros(size(x)); % 預(yù)分配
- for k = 3:length(x)-2
- y(k) = median(x(k-2:k+2));
- end
- 取 x=rand(1,2500);
- 計(jì)時(shí)運(yùn)行:Elapsed time is 0.030949 seconds.
下面我們?cè)囋噧?nèi)嵌。首先,要研究一下你要內(nèi)嵌的函數(shù),本例中就是median。在命令行中輸入:edit median,發(fā)現(xiàn)它是使用sort進(jìn)行工作的。將核心代碼內(nèi)嵌:
- y = zeros(size(x));
- for k = 3:length(x)-2
- tmp = sort(x(k-2:k+2));
- y(k) = tmp(3); ;
- end
- 仍取x=rand(1,2500);
- 計(jì)時(shí)運(yùn)行:Elapsed time is 0.011379 seconds.
以上就是一個(gè)演示,可見時(shí)間確實(shí)省去了不少。為了確認(rèn)你想內(nèi)嵌的函數(shù)是否是用M-File實(shí)現(xiàn)的,你可以使用“edit 函數(shù)名”命令試試看。
網(wǎng)頁題目:讓你的MATLAB代碼飛起來
當(dāng)前地址:http://fisionsoft.com.cn/article/dhdgooh.html


咨詢
建站咨詢
