腾讯奇迹觉醒攻略:MATLAB性能測試框架

奇迹觉醒女神之光 www.mhotr.icu 2019-3-3 16:59| 發布者: ilovematlab| 查看: 21532| 評論: 8|原作者: oopmatlab

摘要: MATLAB Performance Test 框架是Mathworks 在MATLAB R2016a 中推出的?個新的框架,該框架?來獲得代碼性能在統計意義上的數據,還可以?來?較算法的性能,并且給出詳細完整的報告。 ... ... ... ... ... ... ... ...

文章PDF瀏覽下載鏈接

目錄:

為什么需要Performance Test框架

MATLAB Performance Test 框架是Mathworks在MATLAB R2016a中推出的一個新的框架,該框架用來獲得代碼性能在統計意義上的數據,還可以用來比較算法的性能,并且給出詳細完整的報告。 如果只需要定性的性能結果,tic和toc是一個快速簡單的獲得代碼耗時的工具,大家一定都使用過。比如下面的代碼,比較對數組的不同賦值方式,衡量預先分配和不預先分配的耗時差別。test
% alloc_tictoc.m
rows = 1000;
cols = 1000;
X=[];
Y=[];

% 對不預先分配的數組X賦值計時
tic
for r = 1:cols
    for c = 1:rows
        X(r,c) = 1;
    end
end
toc
  
% 對預先分配的數組Y賦值計時
tic
Y = zeros(rows,cols);
for r = 1:cols
    for c = 1:rows
        Y(r,c) = 1;
    end
end  
toc
運行結果可以預料,預先分配數組賦值比不預先分配更快。
% Command Line,fontsize=\small 
>> alloc_tictoc
Elapsed time is 0.449438 seconds.    % 不預先分配
Elapsed time is 0.016257 seconds.    % 預先分配
tic,toc可以快速簡單的獲得定性的結果,但是有時候,在工程計算中需要代碼耗時的定量結果,比如對1000 X 1000的數組賦值,想確切知道預先分配比不預先分配究竟快多少? ,再使用tic toc就捉襟見肘了,運行上述script多次, 可以發現得到的其實是一些隨機的分布的結果。
>> alloc_tictoc
Elapsed time is 0.472567 seconds.
Elapsed time is 0.014476 seconds.
>> alloc_tictoc
Elapsed time is 0.434714 seconds.
Elapsed time is 0.016879 seconds.
>> alloc_tictoc
Elapsed time is 0.448822 seconds.
Elapsed time is 0.012684 seconds.
>> alloc_tictoc
Elapsed time is 0.474179 seconds.
Elapsed time is 0.013808 seconds.
>> alloc_tictoc
Elapsed time is 0.467369 seconds.
Elapsed time is 0.014176 seconds.
定性的來說,可以肯定預先分配數組的方法要快得多,但是每次測量得到的結果,其實是符合一定分布規律的隨機變量{(MATLAB的每一步的計算都要經過確定的函數和優化,從這個角度來說,每次測量應該得到精確唯一的結果。現實中,MATLAB工作在操作系統中,而操作系統會統籌分配系統的計算資源,不同的時刻,資源的分配不一定相同,從而帶來了一定的隨機性。) },測量結果在一定的范圍內波動給獲得定量結果造成困難。當兩個算法的差別不是很大的時候,這樣的波動可能甚至會影響定性的結果。如何得到可靠的性能測量的數據就是我們這章要解決的問題。最容易想到的一個改進就是把運行多次,把每次的結果收集起來,然后求平均,比如:
tic
for iter = 1: 100
  for r = 1:cols
    for c = 1:rows
        X(r,c) = 1;
    end
  end
end
toc
% 再把得到的結果求平均,略
但是循環的次數很難有一個統一的標準,到底循環多少次結果求平均才可靠,次數少了結果不可靠,次數多了浪費時間?;褂?,理論上能否保證提高循環次數就一定可以得到統計意義上可靠的結果?一個嚴謹的性能測試不但需要一套規范的標準,還需要統計理論的支持。 另一個測量性能時要注意的問題是:如下所示,測量結果可能對algorithm1不公平,因為MATLAB的代碼在第一次運行時候會伴隨編譯和優化,比如Just In Time Compilation(JIT)和最新的Language Execution Engine(LXE)的加速,這就是說,前幾次運行的代碼會有一些編譯和優化帶來的的耗時,可以把它們想象成運動之前的熱身,如果algorithm1和algorithm2共用一些的代碼,那么algorithm1運行時,可能已經幫助algorithm2熱了一部分的身,而帶來的額外時間卻算在了algorithm1的耗時內
% 代碼優化可能帶來額外的耗時,fontsize=\small 
% 計時算法1
tic
algorithm1();   
toc

% 計時算法2
tic
algorithm2();
toc
所以更公平的測時方法是,剔除前幾次的運行,讓要比較的代碼都熱完身之后再計時
% 剔除代碼優化可能帶來額外的耗時,fontsize=\small 
% 算法1熱身4次
for iter  = 1:4
 algorithm1()
end
% 計時算法1
tic
algorithm1();
toc
% 算法2熱身4次
for iter  = 1:4
 algorithm2()
end
% 計時算法2
tic
algorithm2();
toc

基于類的(Class-Based)性能測試框架

構造測試類

構造一個基于類的Performance測試很簡單,我們只需要把Performance Test一節中的腳本轉成性能測試中的方法即可。 任何基于類的Performance 測試類都要繼承自matlab.perftest.TestCase父類,也就是框架的提供者;下面的類定義中,還把rows和cols兩個變量放到了類的屬性中,這樣test1和test2可以共享這兩個變量。
% AllocTest, fontsize=\small 
classdef AllocTest < matlab.perftest.TestCase   % 性能測試的公共父類
    properties
        rows = 1000
        cols = 1000 
    end    
    methods(Test)
        % 不預先分配賦值 測試點
        function test1(testCase)    
            for r = 1:testCase.cols
                for c = 1:testCase.rows
                    X(r,c) = 1;
                end
            end
        end        
        % 預先分配賦值 測試點
        function test2(testCase)
            X = zeros(testCase.rows,testCase.cols);         
            for r = 1:testCase.cols
                for c = 1:testCase.rows
                    X(r,c) = 1;
                end
            end
        end
    end
end
運行runperf開始Performance測試
>> r = runperf('AllocTest')
Running AllocTest
..........
.......
Done AllocTest
__________
r = 

  1x2 MeasurementResult array with properties:

    Name
    Valid
    Samples
    TestActivity

Totals:
   2 Valid, 0 Invalid
runperf返回一個1X2的結果對象數組,兩個測試點都是合格的測試,

測試結果解析

在命令行中檢查對象數組中的一個元素,即test1的測試結果
>> r(1)
ans = 
  MeasurementResult with properties:

            Name: 'AllocTest/test1'
           Valid: 1
         Samples: [5x7 table]     %簡報  
    TestActivity: [9x12 table]    %原始數據 
Totals:
   1 Valid, 0 Invalid.
其中屬性TestActivity是測量的所有測量原始數據,原始sample是有用數據的簡報,這里解析TestActivity中的原始數據
>> r(1).TestActivity 

ans = 

         Name          Passed    Failed    Incomplete    MeasuredTime    Objective        
    _______________    ______    ______    __________    ____________    _________  

    AllocTest/test1    true      false     false         0.52387         warmup       
    AllocTest/test1    true      false     false         0.44674         warmup       
    AllocTest/test1    true      false     false         0.50816         warmup       
    AllocTest/test1    true      false     false         0.38104         warmup       
    AllocTest/test1    true      false     false         0.38372         sample       
    AllocTest/test1    true      false     false          0.4197         sample       
    AllocTest/test1    true      false     false         0.38647         sample       
    AllocTest/test1    true      false     false         0.38489         sample       
    AllocTest/test1    true      false     false         0.37503         sample       
測量的結果是一個table對象,從結果中看出,測試一共進行了9次,前4次是這一節尾提到對代碼的熱身,這四次的結果在Objective中標記被做warmup,從數值上也可以大致看出它們和后5次測量有著不同的分布,計算均值的時候需要把它們剔除,正式的測試標記做sample測試,test1的sample測試一共運行了5次。檢查r(2)得到類似的結果:
% Command Line , fontsize = \small 
>> r(2)

ans = 

  MeasurementResult with properties:

            Name: 'AllocTest/test2'
           Valid: 1
         Samples: [4x7 table]      %簡報
    TestActivity: [8x12 table]     %原始數據

Totals:
   1 Valid, 0 Invalid.
>> 
>> r(2).TestActivity

ans = 

         Name          Passed    Failed    Incomplete    MeasuredTime    Objective    
    _______________    ______    ______    __________    ____________    _________    

    AllocTest/test2    true      false     false         0.018707        warmup       
    AllocTest/test2    true      false     false         0.028393        warmup       
    AllocTest/test2    true      false     false         0.013336        warmup       
    AllocTest/test2    true      false     false         0.012915        warmup       
    AllocTest/test2    true      false     false         0.013543        sample       
    AllocTest/test2    true      false     false         0.012904        sample       
    AllocTest/test2    true      false     false         0.012778        sample       
    AllocTest/test2    true      false     false          0.01312        sample       
test2有4次warmup,4次sample測試。 按照默認設置,每個測試點都要先warmup代碼四次,再進入正式的sample測試,有四個sample測試意味著test2這個測試點一共被運行了四次,test2的測試次數和test1的測試次數不同,每個測試點運行幾次是由測量數據集合是否到達統計目標所決定的,誤差范圍和置信區間小節將詳細介紹。 有了多次測量的結果,我們可以利用一個幫助函數,從table中取出sample的數據,
function dispMean(result)
 fullTable = vertcat(result.Samples);
 varfun(@mean,fullTable,'InputVariables','MeasuredTime','GroupingVariables','Name')
end
然后對它們求均值,得到的結果才是統計意義上的測量結果。
>> dispMean(r)

ans = 

         Name          GroupCount    mean_MeasuredTime
    _______________    __________    _________________

    AllocTest/test1    5              0.38996         
    AllocTest/test2    4              0.013086         
如果算法在不斷的變化中,這樣的測量結果也可以保留起來,從而追蹤一段時間之內算法性能的變化。

誤差范圍和置信區間

Performance測試框架規定,一個測試點warmup四次之后,將再運行4到32不等的次數,直到測量數據達到0.05的Relative Margin of Error,0.95的置信區間為止,一但已有的測量值到達了上述的統計目標,就停止計算,如果超過32次還是沒有達到0.05的Relative Margin of Error,框架仍然停止計算,但拋出一個警告。這就是為什么前節的test1運行了5次,而test2只運行了4次,它更快達到統計目標。 在每獲得一次新的測量數據時,已有數據的Relative Margin of Error都將被重新計算,來決定是否需要再次運行測試點。下面的函數(需要統計工具箱) 幫助計算Relative Margin of Error, 用它來計算test1的數據可以驗證相對誤差在得到第4次測量結果仍然大于0.05,直到第5次計算小于0.05,于是停止繼續測量
% 計算Relative Margin of Error的函數, fontsize = \small 
function er = relMarOfEr(data)
 L = length(data);
 er = tinv(0.95,L-1)*std(data)/mean(data)/sqrt(L);
end
>> relMoE(r(1).Samples.MeasuredTime(1:end-1))   % 取test1的第1到第4次測量結果
ans =
    0.0519

>> relMoE(r(1).Samples.MeasuredTime)            % 取test1所有測量結果
ans =
    0.0421
test2的測量結果類似,第4次的測量,整體數據達到統計目標
>> relMoE(r(2).Samples.MeasuredTime(1:end-1))
ans =
    0.0529

>> relMoE(r(2).Samples.MeasuredTime)
ans =

    0.0302
所謂0.95的置信區間,就是說該系列的測量將確定一個區間,有百分之95的幾率實際的真實值就在該區間中。調用函數fitdist得到置信區間(需要統計工具箱。)
>> fitdist(r(1).Samples.MeasuredTime,'Normal')
ans =
  NormalDistribution

  Normal distribution
       mu =  0.389962   [0.368598, 0.411326]   % 0.95置信區間
    sigma = 0.0172059   [0.0103086, 0.049442]
0.05的Margin of Error并不是所有的測試都能達到,事實上我們如果多次運行上述的同一個測試,很有可能test2的結果會有幾次含有Warning。
>> r = runperf('AllocTest')
Running AllocTest
..........
..........
..........
..........
....Warning: The target Relative Margin of Error was not met after running the MaxSamples for
AllocTest/test2. 
 % 測試點運行超過32次任沒有達到統計目標
Done AllocTest
__________

r = 

  1x2 MeasurementResult array with properties:

    Name
    Valid
    Samples
    TestActivity

Totals:
   2 Valid, 0 Invalid.
>> 
>> r(2)

ans = 

  MeasurementResult with properties:

            Name: 'AllocTest/test2'
           Valid: 1
         Samples: [32x7 table]
    TestActivity: [36x12 table]    % test2運行了一共4+32=36次

Totals:
   1 Valid, 0 Invalid.
Warning說明測量的操作過于的細微,噪音影響過大。我們可以通過增大計算量,或者放松統計目標來避免這個Warning,比如修改默認的Relative Margin of Error
% 增大Relative Margion of Error, fontsize = \small 
>> import matlab.perftest.TimeExperiment
>> experiment = TimeExperiment.limitingSamplingError('RelativeMarginOfError',0.10);
>> suite = testsuite('AllocTest');
>> run(experiment,suite)
Running AllocTest
..........
......
Done AllocTest
__________


ans = 

  1x2 MeasurementResult array with properties:

    Name
    Valid
    Samples
    TestActivity

Totals:
   2 Valid, 0 Invalid.

性能測試的適用范圍討論

性能測試框架最初是Mathworks內部使用的一個框架,使用范圍和單元測試一致,單元測試保證在算法的進化過程中,功能不退化;而性能測試保證算法的性能不退化。這樣一個框架對MATLAB用戶的算法開發顯然會帶來價值,但是我們要分清什么樣的測量才是有價值的,構造測試類中的例子是一個簡單易懂的例子,但作為MATLAB的用戶,我們其實沒有必要去測量和記錄這些簡單的MATLAB的操作的性能(這是Mathworks內部性能測試的主要工作) ,我們只需要記住它們定性的結果,比如給數組賦值之前要先分配,運算盡量向量化等等就可以了。性能測試框架真正能給我們帶來的價值的用例,是如下的測試實際算法性能的情況,在用戶的算法myAlgorithm的開發過程中,我們可以定期的運行該測試文件,保證性能不退化
classdef AlgoTest1 < matlab.perftest.TestCase
    methods(Test)
        function test1(testCase)   
               myAlgorithm();
        end
    end
end
或者比較兩個算法,algorithm1可以代表一個舊的算法,algorithm2代表新的改進的算法,依靠Performance Testing 框架,我們可以得到可靠的數據到底algorithm2改進了多少
classdef AlgoTest1 < matlab.perftest.TestCase
    methods(Test)
        function test1(testCase)   
               algorithm1();
        end
        
        function test2(testCase)            
               algorithm2();
        end
    end
end


關于作者

oopmatlab,計算物理博士,計算機碩士,《MATLAB面向對象編程-從入門到設計模式》作者,現就職一家科學工程計算公司的任架構組C++軟件工程師。業余興趣包括如何把軟件工程中的現代手段,應用到科學和工程計算當中去,來更好的解決復雜的問題。包括如何從整體上設計科學計算程序的結構;如何讓程序便于擴展和修改;在改進和開發算法的時候,如何保證程序已有的功能沒有收到影響;如何讓算法開發和測試系統的建立齊頭并進。

聲明:本版塊所有文章版權歸作者個人所有,未經允許,不得作為出版物出版。如需轉載,請聯系論壇管理員。

相關閱讀

發表評論

最新評論

引用 無是無非 2019-8-2 09:45
很好很好
引用 小象同志 2019-6-9 16:10
學到了
引用 阿萊克修斯傳 2019-3-9 17:07
學到了
引用 川川2050 2019-1-4 19:20
明白了
引用 stephenliu 2018-12-18 10:17
非常實用的技術文章,學習一下
引用 yyh1020 2018-12-13 15:38
學習了
引用 1216665433 2018-11-20 16:17
現在還不會,將來會有用!
引用 聯盟獵人qhd 2018-10-31 09:23
哇~~~~~~~~
引用 Evangline-Y 2018-10-21 15:33
學習到了
引用 cdjy6481 2018-10-14 15:05
膜拜,現在還只是學習階段
引用 autoxc 2018-7-2 13:28
學習了
引用 shixinmeng 2018-5-18 11:32
這是一篇有用的文章
引用 覃港先 2018-5-4 17:00
學習學習
引用 Noir二號 2018-4-27 10:07
學習受教了
引用 囗果郭 2018-4-20 09:16
支持了 感謝分享
引用 大佬高 2018-4-11 00:05
講解的非常詳細,感謝分享,厲害厲害
引用 studying2018 2018-4-8 22:44
學習了
引用 TCCMATLAB 2018-4-1 20:12
再次感受到MATLAB的強大,學習,感謝分享。
引用 zhonglinlin 2018-4-1 15:42
學習了
引用 mengf2688 2018-3-13 14:15
6666666666666666,講解的十分詳細

查看全部評論(8)

MATLAB table數據結構 首篇

MATLAB table是R2013b中引入的一個新的數據結構,雖然不像常用的基本數據類型為人熟悉,但是在編程中非常有用。它用來存放表狀類型的數據結構,并且支持常見的表和表之間的運算。 ... ... ... ... ... ... ...

MATLAB映射表數據結構

除了常用的基本數據類型,MATLAB還有很多其它實用的數據類型不為人熟悉,例如映射表containers.Map,常用的MATLAB高級數據類型。它最大的特點使用方便的索引方式進行快速的查找。本篇介紹為什么需要這種數據結構,以 ...

MATLAB table數據結構 再篇

MATLAB table是R2013b中引入的一個新的數據結構,雖然不像常用的基本數據類型為人熟悉,但是在編程中非常有用。它用來存放表狀類型的數據結構,并且支持常見的表和表之間的運算。 ... ... ... ... ... ... ...

對函數的輸入進行檢查和解析

在工程計算中,如果函數的輸入有錯誤,我們總是希望能盡早捕捉到這些錯誤,并及時終止程序。在MATLAB 中,可以使用validateattributes,validatestring和inputParser 類來對輸入進行檢查。它們提供全面的檢查功能和清 ...

MATLAB單元測試

本篇是把現代軟件工程思想應用到MATLAB工程開發中的精髓,希望高級MATLAB用戶仔細研讀。作者用實際的例子解釋在開發和逐漸改進算法的時候,如何保證程序已有的功能沒有收到影響,步步為營,讓算法開發和測試系統的建 ...

MATLAB映射表數據結構

除了常用的基本數據類型,MATLAB還有很多其它實用的數據類型不為人熟悉,例如映射表containers.Map,常用的MATLAB高級數據類型。它最大的特點使用方便的索引方式進行快速的查找。本篇介紹為什么需要這種數據結構,以 ...

對函數的輸入進行檢查和解析

在工程計算中,如果函數的輸入有錯誤,我們總是希望能盡早捕捉到這些錯誤,并及時終止程序。在MATLAB 中,可以使用validateattributes,validatestring和inputParser 類來對輸入進行檢查。它們提供全面的檢查功能和清 ...

MATLAB性能測試框架

MATLAB Performance Test 框架是Mathworks 在MATLAB R2016a 中推出的?個新的框架,該框架?來獲得代碼性能在統計意義上的數據,還可以?來?較算法的性能,并且給出詳細完整的報告。 ... ... ... ... ... ... ... ...
關閉

站長推薦上一條 /4 下一條

返回頂部