MATLAB的OOP速度慢还是我做错了什么?

我正在做MATLAB OOP的试验,作为一个开始,我模仿了我的C ++的Logger类,我将所有的string帮助器函数都放在一个String类中,认为能够做一些事情是很棒的,比如a + ba == ba.find( b )而不是strcat( ab )strcmp( a, b ) ,检索strfind( a, b )第一个元素strfind( a, b )等。

问题:放缓

我把上面的东西都用上了,马上注意到一个急剧的放缓。 我做错了(这当然是可能的,因为我有相当有限的MATLAB经验),还是MATLAB的OOP只是引入了大量的开销?

我的testing案例

这里是我为string做的简单的testing,基本上只是附加一个string,并再次删除附加部分:

 classdef String < handle .... properties stringobj = ''; end function o = plus( o, b ) o.stringobj = [ o.stringobj b ]; end function n = Length( o ) n = length( o.stringobj ); end function o = SetLength( o, n ) o.stringobj = o.stringobj( 1 : n ); end end function atest( a, b ) %plain functions n = length( a ); a = [ ab ]; a = a( 1 : n ); function btest( a, b ) %OOP n = a.Length(); a = a + b; a.SetLength( n ); function RunProfilerLoop( nLoop, fun, varargin ) profile on; for i = 1 : nLoop fun( varargin{ : } ); end profile off; profile report; a = 'test'; aString = String( 'test' ); RunProfilerLoop( 1000, @(x,y)atest(x,y), a, 'appendme' ); RunProfilerLoop( 1000, @(x,y)btest(x,y), aString, 'appendme' ); 

结果

总计时间(以秒为单位),进行1000次迭代:

btest 0.550(使用String.SetLength 0.138,String.plus 0.065,String.Length 0.057)

atest 0.015

logging器系统的结果同样如此:在1000次调用frpintf( 1, 'test\n' )为0.1秒,在内部使用String类时调用我的系统1000次(7秒)(确定,它有更多逻辑,但是与C ++比较:我的系统在输出端使用std::string( "blah" )std::cout的开销与普通std::cout << "blah"是一样的1毫秒)。

查找类/包函数时是否只是开销?

由于MATLAB被解释了,它必须在运行时查找函数/对象的定义。 所以我想知道,查找类或包函数与path中的函数可能涉及更多的开销。 我试图testing这个,只是变得陌生。 为了排除类/对象的影响,我比较了在path中调用函数与包中函数的关系:

 function n = atest( x, y ) n = ctest( x, y ); % ctest is in matlab path function n = btest( x, y ) n = util.ctest( x, y ); % ctest is in +util directory, parent directory is in path 

结果,收集了与上面相同的方式:

testing0.004秒,ctest为0.001秒

btest 0.060秒,在util.ctest中0.014秒

所以,这只是MATLAB花费时间来查找OOP实现的定义的开销,而这个开销并不是直接在path中的函数?

我一直在使用OO MATLAB一段时间,最后看到类似的性能问题。

简单的回答是:是的,MATLAB的面向对象是一种缓慢。 有相当大的方法调用开销,高于主stream的OO语言,并且你可以做的事情不多。 部分原因可能是惯用的MATLAB使用“向量化”代码来减less方法调用的次数,并且每个通话开销并不是高优先级。

我通过写作什么都不做的“nop”函数作为各种函数和方法的基准。 以下是一些典型的结果。

 >> call_nops
计算机:PCWIN发布:2009b
调用每个函数/方法100000次
 nop()函数:0.02261秒每次调用0.23次
 nop1-5()函数:0.02182秒每次调用0.22次
 nop()子函数:0.02244秒每次调用0.22次
 @()[]匿名函数:0.08461秒每次调用0.85次
 nop(obj)方法:0.24664秒2.47每次调用usec
 nop1-5(obj)方法:0.23469秒每次调用2.35次
 nop()私有函数:0.02197秒每次调用0.22次
 classdef nop(obj):0.90547秒每次调用9.05次
 classdef obj.nop():1.75522秒17.55每次调用usec
 classdef private_nop(obj):0.84738 sec 8.47每次调用usec
 classdef nop(obj)(m文件):0.90560秒每次调用9.06次
 classdef class.staticnop():1.16361 sec 11.64每次调用usec
 Java nop():2.43035秒每次调用24.30次
 Java static_nop():0.87682秒8.77每次调用的usec
来自Java的Java nop():0.00014秒每次调用0.00次
墨西哥mexnop():0.11409秒每次通话1.14美元
 C nop():0.00001秒每次调用0.00次

R2008a到R2009b的结果类似。 这是在运行32位MATLAB的Windows XP x64上。

“Java nop()”是一个在M代码循环内调用的无所作为的Java方法,在每次调用时都包含MATLAB到Java的调度开销。 “来自Java的Java nop()”与Java for()循环中调用的相同,不会导致边界惩罚。 用一粒盐来拿Java和C的时间; 一个聪明的编译器可以完全优化掉电。

包的范围机制是新的,几乎与classdef类同时引入。 它的行为可能是相关的。

几个试探性的结论:

  • 方法比函数慢。
  • 新风格(classdef)方法比旧风格方法慢。
  • 新的obj.nop()语法比nop(obj)语法慢,即使对于classdef对象也是如此。 Java对象相同(未显示)。 如果你想快点,请拨打nop(obj)
  • Windows上的64位MATLAB中的方法调用开销较高(约2倍)。 (未显示)
  • MATLAB方法调度比其他一些语言慢。

说这是为什么这只是我的猜测。 MATLAB引擎的OO内部结构是不公开的。 这本身不是一个解释的vs编译的问题 – MATLAB有一个JIT–但是MATLAB的宽松input和语法可能意味着更多的运行时间。 (例如,从句法本身来看,“f(x)”是一个函数调用还是一个数组中的索引,它在运行时依赖于工作空间的状态)。这可能是因为MATLAB的类定义是绑定的到许多其他语言的方式不是文件系统状态。

那么,该怎么办?

一个惯用的MATLAB方法就是通过构造你的类定义来“vector化”你的代码,使得一个对象实例包装一个数组; 也就是说,它的每个字段都保持平行的数组(在MATLAB文档中称为“平面”组织)。 而不是有一个对象数组,每个对象都有标量值的字段,定义它们本身是数组的对象,并让这些方法将数组作为input,并对字段和input进行vector化调用。 这减less了方法调用的次数,希望足以使调度开销不是瓶颈。

在MATLAB中模仿C ++或Java类可能不是最佳的。 Java / C ++类通常被构build为使得对象是最小的构build块(尽可能具体)(也就是,许多不同的类),然后将它们组合到数组,集合对象等中,并用循环遍历它们。 要做出快速的MATLAB类,把这个方法从里面翻出来。 有更大的类,其字段是数组,并在这些数组上调用vector化方法。

关键是要安排你的代码发挥语言的优势 – 数组处理,vectormath – 避免弱点。

编辑:由于原来的职位,R2010b和R2011a已经出来。 整体情况是一样的,MCOS调用速度更快,而Java和老式的方法调用越来越

编辑:我曾经有一些笔记这里的“path灵敏度”与一个额外的函数调用时间表,function时间受到如何configurationMatlabpath的影响,但似乎是我的特定networking设置的像差时间。 上面的图表反映了我的testing随着时间的推移典型的时间。

更新:R2011b

编辑(2/13/2012):R2011b出来了,而且performance图片已经改变了足以更新这个。

 Arch:PCWIN发布:2011b 
机器:R2011b,Windows XP,8x Core i7-2600 @ 3.40GHz,3 GB RAM,NVIDIA NVS 300
每次操作10万次
每通电话总共为μsec
 nop()函数:0.01578 0.16
 nop(),10倍循环展开:0.01477 0.15
 nop(),100x循环展开:0.01518 0.15
 nop()子函数:0.01559 0.16
 @()[]匿名函数:0.06400 0.64
 nop(obj)方法:0.28482 2.85
 nop()私有函数:0.01505 0.15
 classdef nop(obj):0.43323 4.33
 classdef obj.nop():0.81087 8.11
 classdef private_nop(obj):0.32272 3.23
 classdef class.staticnop():0.88959 8.90
 classdef常数:1.51890 15.19
 classdef属性:0.12992 1.30
使用getter的classdef属性:1.39912 13.99
 + pkg.nop()函数:0.87345 8.73
 + pkg.nop()from + pkg:0.80501 8.05
 Java obj.nop():1.86378 18.64
 Java nop(obj):0.22645 2.26
 Java feval('nop',obj):0.52544 5.25
 Java Klass.static_nop():0.35357 3.54
 Java的obj.nop():0.00010 0.00
 MEX mexnop():0.08709 0.87
 C nop():0.00001 0.00
 j()(内build值):0.00251 0.03

我认为这个结果是:

  • MCOS / classdef方法更快。 现在,只要使用foo(obj)语法,成本就可以与旧式类相提并论。 所以方法速度不再是在大多数情况下坚持使用旧式类的理由。 (荣誉,MathWorks!)
  • 将函数放入命名空间会使它们变慢。 (在R2011b中不是新的,在我的testing中只是新的。)

更新:R2014a

我重build了基准代码并在R2014a上运行。

 PCWIN64上的Matlab R2014a  
 PCWIN64上的Matlab 8.3.0.532(R2014a)/ Java 1.7.0_11 Windows 7 6.1(eilonwy-win7) 
机器:Core i7-3615QM CPU @ 2.30GHz,4GB内存(VMware Virtual Platform)
 nIters = 100000 

操作时间(微秒)  
 nop()函数:0.14 
 nop()子函数:0.14 
 @()[]匿名函数:0.69 
 nop(obj)方法:3.28 
 nop()private @ fcn on @class:0.14 
 classdef nop(obj):5.30 
 classdef obj.nop():10.78 
 classdef pivate_nop(obj):4.88 
 classdef class.static_nop():11.81 
 classdef常数:4.18 
 classdef属性:1.18 
使用getter的classdef属性:19.26 
 + pkg.nop()函数:4.03 
 + pkg.nop()from + pkg:4.16 
 feval('nop'):2.31 
 feval(@nop):0.22 
 eval('nop'):59.46 
 Java obj.nop():26.07 
 Java nop(obj):3.72 
 Java feval('nop',obj):9.25 
 Java Klass.staticNop():10.54 
来自Java的Java obj.nop():0.01 
 MEX mexnop():0.91 
内buildj():0.02 
结构s.foo字段访问:0.14 
 isempty(持久性):0.00 

更新:R2015b:物体变快了!

这是R1515b的结果,由@Shaked友情提供。 这是一个很大的变化:OOP速度明显加快,现在obj.method()语法和method(obj)一样快,比传统的OOP对象快得多。

 PCWIN64上的Matlab R2015b  
 Matlab 8.6.0.267246(R2015b)/ PCWIN64上的Java 1.7.0_60 Windows 8 6.2(nanit-shaked) 
机器:Core i7-4720HQ CPU @ 2.60GHz,16 GB RAM(20378)
 nIters = 100000 

操作时间(微秒)  
 nop()函数:0.04 
 nop()子函数:0.08 
 @()[]匿名函数:1.83 
 nop(obj)方法:3.15 
 nop()私人fcn on @class:0.04 
 classdef nop(obj):0.28 
 classdef obj.nop():0.31 
 classdef pivate_nop(obj):0.34 
 classdef class.static_nop():0.05 
 classdef常数:0.25 
 classdef属性:0.25 
 classdef属性与getter:0.64 
 + pkg.nop()函数:0.04 
 + pkg.nop()from + pkg:0.04 
 feval('nop'):8.26 
 feval(@nop):0.63 
 eval('nop'):21.22 
 Java obj.nop():14.15 
 Java nop(obj):2.50 
 Java feval('nop',obj):10.30 
 Java Klass.staticNop():24.48 
来自Java的Java obj.nop():0.01 
 MEX mexnop():0.33 
内含物j():0.15 
结构s.foo字段访问:0.25 
 isempty(持续):0.13 

“基准源代码”

我已经把这些基准testing的源代码放在GitHub上,在MIT许可下发布。 https://github.com/apjanke/matlab-bench

处理类有一个额外的开销,跟踪所有的引用自身的清理目的。

尝试不使用句柄类的相同实验,看看你的结果是什么。

OO的性能在很大程度上取决于所使用的MATLAB版本。 我不能评论所有版本,但从经验中知道2012a比2010版本有了很大的改进。 没有基准,所以没有数字来呈现。 我的代码,完全使用句柄类编写,并在2012a下编写,根本不会在早期版本下运行。

其实你的代码没有问题,但它是一个与Matlab的问题。 我觉得这是一种玩耍的样子。 编译类代码只是开销。 我已经做了简单的类的testing(一次作为处理)和其他(一次作为价值课)

  classdef Pointh < handle properties X Y end methods function p = Pointh (x,y) pX = x; pY = y; end function d = dist(p,p1) d = (pX - p1.X)^2 + (pY - p1.Y)^2 ; end end end 

这里是testing

 %handle points ph = Pointh(1,2); ph1 = Pointh(2,3); %values points p = Pointh(1,2); p1 = Pointh(2,3); % vector points pa1 = [1 2 ]; pa2 = [2 3 ]; %Structur points Ps.X = 1; Ps.Y = 2; ps1.X = 2; ps1.Y = 3; N = 1000000; tic for i =1:N ph.dist(ph1); end t1 = toc tic for i =1:N p.dist(p1); end t2 = toc tic for i =1:N norm(pa1-pa2)^2; end t3 = toc tic for i =1:N (Ps.X-ps1.X)^2+(Ps.Y-ps1.Y)^2; end t4 = toc 

结果t1 =

12.0212%把手

t2 =

价值12.0042%

t3 =

 0.5489 % vector 

t4 =

 0.0707 % structure 

因此,为了高效的性能,避免使用OOP代替结构是组variables的好select