![]() 文章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 %188bet官网1热身4次 for iter = 1:4 algorithm1() end % 计时算法1 tic algorithm1(); toc %188bet官网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 Invalidrunperf回来一个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 sampletest2有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.0421test2的丈量成果相似,第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++软件工程师。业余爱好包含怎么把软件工程中的现代手法,应用到科学和工程核算当中去,来更好的处理杂乱的问题。包含怎么从全体上规划科学核算程序的结构;怎么让程序便于扩展和修正;在改善和开发算法的时分,怎么确保程序已有的功用没有收到影响;怎么让算法开发和测验体系的树立齐头并进。声明:本版块一切文章版权归作者个人一切,未经答应,不得作为出书物出书。如需转载,请联络论坛管理员。 |