是MATLAB OOP运行缓慢还是我做错了什么? [英] Is MATLAB OOP slow or am I doing something wrong?

查看:95
本文介绍了是MATLAB OOP运行缓慢还是我做错了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试 MATLAB

I'm experimenting with MATLAB OOP, as a start I mimicked my C++'s Logger classes and I'm putting all my string helper functions in a String class, thinking it would be great to be able to do things like a + b, a == b, a.find( b ) instead of strcat( a b ), strcmp( a, b ), retrieve first element of strfind( a, b ), etc.

问题:速度变慢

我使用了以上内容,并立即注意到急剧的速度下降.我做错了吗(由于我有限的MATLAB经验,这肯定是可能的),还是MATLAB的OOP只是引入了很多开销?

I put the above things to use and immediately noticed a drastic slowdown. Am I doing it wrong (which is certainly possible as I have rather limited MATLAB experience), or does MATLAB's OOP just introduce a lot of overhead?

我的测试用例

这是我对字符串进行的简单测试,基本上只是添加一个字符串,然后再次删除添加的部分:

Here's the simple test I did for string, basically just appending a string and removing the appended part again:

注意:实际不要在真实代码中编写这样的String类! Matlab现在具有本机string数组类型,您应该改用它.

Note: Don't actually write a String class like this in real code! Matlab has a native string array type now, and you should use that instead.

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 = [ a b ];
  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次迭代:

Total time in seconds, for 1000 iterations:

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

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

最新0.015

记录器系统的结果也是如此:1000次通话为0.1秒 内部使用String类时,对frpintf( 1, 'test\n' )进行7次(!)秒可进行1000次对我的系统的调用(好的,它具有更多的逻辑,但与C ++相比:使用std::string( "blah" )的系统的开销并且输出端的std::cout与普通的std::cout << "blah"大约为1毫秒.)

Results for the logger system are likewise: 0.1 seconds for 1000 calls to frpintf( 1, 'test\n' ), 7 (!) seconds for 1000 calls to my system when using the String class internally (OK, it has a lot more logic in it, but to compare with C++: the overhead of my system that uses std::string( "blah" ) and std::cout at the output side vs plain std::cout << "blah" is on the order of 1 millisecond.)

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

由于MATLAB被解释,因此它必须在运行时查找函数/对象的定义.所以我想知道查找类或包函数与路径中的函数可能涉及更多的开销.我试图对此进行测试,但它变得越来越陌生.为了排除类/对象的影响,我比较了在路径中调用函数与在包中调用函数:

Since MATLAB is interpreted, it has to look up the definition of a function/object at run time. So I was wondering that maybe much more overhead is involved in looking up class or package function vs functions that are in the path. I tried to test this, and it just gets stranger. To rule out the influence of classes/objects, I compared calling a function in the path vs a function in a package:

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

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

Results, gathered same way as above:

最快0.004秒,在ctest中为0.001秒

atest 0.004 sec, 0.001 sec in ctest

btest 0.060秒,util.ctest中0.014秒

btest 0.060 sec, 0.014 sec in util.ctest

那么,所有这些开销仅仅是来自MATLAB花时间查找其OOP实现的定义,而对于直接在路径中的函数却没有这种开销吗?

So, is all this overhead just coming from MATLAB spending time looking up definitions for its OOP implementation, whereas this overhead is not there for functions that are directly in the path?

推荐答案

我使用OO MATLAB已有一段时间了,最​​终发现了类似的性能问题.

I've been working with OO MATLAB for a while, and ended up looking at similar performance issues.

简短的答案是:是的,MATLAB的OOP有点慢.方法调用的开销很大,比主流的OO语言要高,并且您无能为力.部分原因可能是惯用的MATLAB使用矢量化"代码来减少方法调用的次数,并且每次调用的开销并不是很高的优先级.

The short answer is: yes, MATLAB's OOP is kind of slow. There is substantial method call overhead, higher than mainstream OO languages, and there's not much you can do about it. Part of the reason may be that idiomatic MATLAB uses "vectorized" code to reduce the number of method calls, and per-call overhead is not a high priority.

我通过编写"nop"函数作为各种类型的函数和方法来对性能进行基准测试.这是一些典型的结果.

I benchmarked the performance by writing do-nothing "nop" functions as the various types of functions and methods. Here are some typical results.


>> call_nops
Computer: PCWIN   Release: 2009b
Calling each function/method 100000 times
nop() function:                 0.02261 sec   0.23 usec per call
nop1-5() functions:             0.02182 sec   0.22 usec per call
nop() subfunction:              0.02244 sec   0.22 usec per call
@()[] anonymous function:       0.08461 sec   0.85 usec per call
nop(obj) method:                0.24664 sec   2.47 usec per call
nop1-5(obj) methods:            0.23469 sec   2.35 usec per call
nop() private function:         0.02197 sec   0.22 usec per call
classdef nop(obj):              0.90547 sec   9.05 usec per call
classdef obj.nop():             1.75522 sec  17.55 usec per call
classdef private_nop(obj):      0.84738 sec   8.47 usec per call
classdef nop(obj) (m-file):     0.90560 sec   9.06 usec per call
classdef class.staticnop():     1.16361 sec  11.64 usec per call
Java nop():                     2.43035 sec  24.30 usec per call
Java static_nop():              0.87682 sec   8.77 usec per call
Java nop() from Java:           0.00014 sec   0.00 usec per call
MEX mexnop():                   0.11409 sec   1.14 usec per call
C nop():                        0.00001 sec   0.00 usec per call

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

Similar results on R2008a through R2009b. This is on Windows XP x64 running 32-bit MATLAB.

"Java nop()"是在M代码循环内调用的虚无Java方法,并且每次调用都包括从MATLAB到Java的调度开销. 来自Java的Java nop()"是在Java for()循环中调用的同一件事,不会造成边界损失.用一点时间来了解Java和C的计时;聪明的编译器可以完全优化调用.

The "Java nop()" is a do-nothing Java method called from within an M-code loop, and includes the MATLAB-to-Java dispatch overhead with each call. "Java nop() from Java" is the same thing called in a Java for() loop and doesn't incur that boundary penalty. Take the Java and C timings with a grain of salt; a clever compiler could optimize the calls away completely.

程序包作用域机制是新的,与classdef类几乎同时引入.其行为可能与之相关.

The package scoping mechanism is new, introduced at about the same time as the classdef classes. Its behavior may be related.

一些初步结论:

  • 方法比函数要慢.
  • 新样式(classdef)方法比旧样式方法慢.
  • 即使对于classdef对象使用相同的方法,新的obj.nop()语法也比nop(obj)语法慢.与Java对象相同(未显示).如果想快速前进,请致电nop(obj).
  • 在Windows上的64位MATLAB中,方法调用开销较高(约为2倍). (未显示.)
  • MATLAB方法分派比其他一些语言慢.
  • Methods are slower than functions.
  • New style (classdef) methods are slower than old style methods.
  • The new obj.nop() syntax is slower than the nop(obj) syntax, even for the same method on a classdef object. Same for Java objects (not shown). If you want to go fast, call nop(obj).
  • Method call overhead is higher (about 2x) in 64-bit MATLAB on Windows. (Not shown.)
  • MATLAB method dispatch is slower than some other languages.

说出为什么会这样只是我的猜测. MATLAB引擎的OO内部构件不是公开的.从本质上来说,这不是一个解释还是编译的问题-MATLAB有一个JIT-但MATLAB较宽松的类型和语法可能意味着在运行时需要进行更多工作. (例如,不能仅从语法上确定"f(x)"是函数调用还是数组索引;它取决于运行时工作空间的状态.)这可能是因为MATLAB的类定义已绑定以其他许多语言都没有的方式进入文件系统状态.

Saying why this is so would just be speculation on my part. The MATLAB engine's OO internals aren't public. It's not an interpreted vs compiled issue per se - MATLAB has a JIT - but MATLAB's looser typing and syntax may mean more work at run time. (E.g. you can't tell from syntax alone whether "f(x)" is a function call or an index into an array; it depends on the state of the workspace at run time.) It may be because MATLAB's class definitions are tied to filesystem state in a way that many other languages' are not.

那该怎么办?

一种惯用的MATLAB方法是通过构造类定义以使对象实例包装数组来矢量化"代码.也就是说,它的每个字段都包含并行数组(在MATLAB文档中称为平面"组织).与其拥有一个对象数组,而不是每个对象都具有标量值的字段,不如定义对象本身就是数组,并让方法将数组作为输入,并对字段和输入进行矢量化调用.这样可以减少方法调用的次数,希望可以使派发开销不会成为瓶颈.

An idiomatic MATLAB approach to this is to "vectorize" your code by structuring your class definitions such that an object instance wraps an array; that is, each of its fields hold parallel arrays (called "planar" organization in the MATLAB documentation). Rather than having an array of objects, each with fields holding scalar values, define objects which are themselves arrays, and have the methods take arrays as inputs, and make vectorized calls on the fields and inputs. This reduces the number of method calls made, hopefully enough that the dispatch overhead is not a bottleneck.

在MATLAB中模仿C ++或Java类可能不是最佳选择. Java/C ++类通常是这样构建的,即对象是最小的构建块,即尽可能具体的对象(即,许多不同的类),您可以将它们组合成数组,集合对象等,并使用循环对其进行迭代.要制作快速的MATLAB类,请将这种方法完全颠倒.拥有更大的类,它们的字段是数组,并在这些数组上调用向量化方法.

Mimicking a C++ or Java class in MATLAB probably won't be optimal. Java/C++ classes are typically built such that objects are the smallest building blocks, as specific as you can (that is, lots of different classes), and you compose them in arrays, collection objects, etc, and iterate over them with loops. To make fast MATLAB classes, turn that approach inside out. Have larger classes whose fields are arrays, and call vectorized methods on those arrays.

关键是安排您的代码以发挥语言的优势-数组处理,矢量化数学-并避免薄弱环节.

The point is to arrange your code to play to the strengths of the language - array handling, vectorized math - and avoid the weak spots.

自原始发布以来,R2010b和R2011a已经问世.总体情况是一样的,MCOS调用变得更快,而Java和旧方法调用变得更慢.

Since the original post, R2010b and R2011a have come out. The overall picture is the same, with MCOS calls getting a bit faster, and Java and old-style method calls getting slower.

我以前在路径敏感性"上有一些注释,并附有函数调用时序的附加表,其中函数时间受Matlab路径配置方式的影响,但这似乎是我的特殊情况当时的网络设置.上面的图表反映了我的测试在一段时间内占优势的典型时间.

I used to have some notes here on "path sensitivity" with an additional table of function call timings, where function times were affected by how the Matlab path was configured, but that appears to have been an aberration of my particular network setup at the time. The chart above reflects the times typical of the preponderance of my tests over time.

编辑(2/13/2012):R2011b已发布,并且性能图片已更改为足以对其进行更新.

EDIT (2/13/2012): R2011b is out, and the performance picture has changed enough to update this.


Arch: PCWIN   Release: 2011b 
Machine: R2011b, Windows XP, 8x Core i7-2600 @ 3.40GHz, 3 GB RAM, NVIDIA NVS 300
Doing each operation 100000 times
style                           total       µsec per call
nop() function:                 0.01578      0.16
nop(), 10x loop unroll:         0.01477      0.15
nop(), 100x loop unroll:        0.01518      0.15
nop() subfunction:              0.01559      0.16
@()[] anonymous function:       0.06400      0.64
nop(obj) method:                0.28482      2.85
nop() private function:         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 constant:              1.51890     15.19
classdef property:              0.12992      1.30
classdef property with getter:  1.39912     13.99
+pkg.nop() function:            0.87345      8.73
+pkg.nop() from inside +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() from Java:       0.00010      0.00
MEX mexnop():                   0.08709      0.87
C nop():                        0.00001      0.00
j() (builtin):                  0.00251      0.03

我认为这样做的结果是:

I think the upshot of this is that:

  • MCOS/classdef方法更快.现在,只要使用foo(obj)语法,成本就与旧样式类差不多.因此,在大多数情况下,方法速度不再是坚持使用旧样式类的原因. (荣誉,MathWorks!)
  • 将函数放入名称空间会使它们变慢. (R2011b中不是新功能,只是我的测试中新功能.)
  • MCOS/classdef methods are faster. Cost is now about on par with old style classes, as long as you use the foo(obj) syntax. So method speed is no longer a reason to stick with old style classes in most cases. (Kudos, MathWorks!)
  • Putting functions in namespaces makes them slow. (Not new in R2011b, just new in my test.)

我已经重构了基准测试代码并在R2014a上运行.

I've reconstructed the benchmarking code and run it on R2014a.


Matlab R2014a on PCWIN64  
Matlab 8.3.0.532 (R2014a) / Java 1.7.0_11 on PCWIN64 Windows 7 6.1 (eilonwy-win7) 
Machine: Core i7-3615QM CPU @ 2.30GHz, 4 GB RAM (VMware Virtual Platform)
nIters = 100000 

Operation                        Time (µsec)  
nop() function:                         0.14 
nop() subfunction:                      0.14 
@()[] anonymous function:               0.69 
nop(obj) method:                        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 constant:                      4.18 
classdef property:                      1.18 
classdef property with getter:         19.26 
+pkg.nop() function:                    4.03 
+pkg.nop() from inside +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 obj.nop() from Java:               0.01 
MEX mexnop():                           0.91 
builtin j():                            0.02 
struct s.foo field access:              0.14 
isempty(persistent):                    0.00 

更新:R2015b:对象变快了!

这里是R2015b的结果,由@Shaked提供.这是一个更改:OOP明显更快,现在obj.method()语法与method(obj)一样快,并且比传统OOP对象快得多.

Update: R2015b: Objects got faster!

Here's R2015b results, kindly provided by @Shaked. This is a big change: OOP is significantly faster, and now the obj.method() syntax is as fast as method(obj), and much faster than legacy OOP objects.


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

Operation                        Time (µsec)  
nop() function:                         0.04 
nop() subfunction:                      0.08 
@()[] anonymous function:               1.83 
nop(obj) method:                        3.15 
nop() private 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 constant:                      0.25 
classdef property:                      0.25 
classdef property with getter:          0.64 
+pkg.nop() function:                    0.04 
+pkg.nop() from inside +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 obj.nop() from Java:               0.01 
MEX mexnop():                           0.33 
builtin j():                            0.15 
struct s.foo field access:              0.25 
isempty(persistent):                    0.13 

更新:R2018a

这是R2018a的结果.在R2015b中引入新执行引擎时,这并不是我们看到的巨大飞跃,但与去年相比仍是可观的改进.值得注意的是,匿名函数处理变得更快.

Update: R2018a

Here's R2018a results. It's not the huge jump that we saw when the new execution engine was introduced in R2015b, but it's still an appreciable year over year improvement. Notably, anonymous function handles got way faster.


Matlab R2018a on MACI64  
Matlab 9.4.0.813654 (R2018a) / Java 1.8.0_144 on MACI64 Mac OS X 10.13.5 (eilonwy) 
Machine: Core i7-3615QM CPU @ 2.30GHz, 16 GB RAM 
nIters = 100000 

Operation                        Time (µsec)  
nop() function:                         0.03 
nop() subfunction:                      0.04 
@()[] anonymous function:               0.16 
classdef nop(obj):                      0.16 
classdef obj.nop():                     0.17 
classdef pivate_nop(obj):               0.16 
classdef class.static_nop():            0.03 
classdef constant:                      0.16 
classdef property:                      0.13 
classdef property with getter:          0.39 
+pkg.nop() function:                    0.02 
+pkg.nop() from inside +pkg:            0.02 
feval('nop'):                          15.62 
feval(@nop):                            0.43 
eval('nop'):                           32.08 
Java obj.nop():                        28.77 
Java nop(obj):                          8.02 
Java feval('nop',obj):                 21.85 
Java Klass.staticNop():                45.49 
Java obj.nop() from Java:               0.03 
MEX mexnop():                           3.54 
builtin j():                            0.10 
struct s.foo field access:              0.16 
isempty(persistent):                    0.07 

更新:R2018b和R2019a:无变化

无明显变化.我不愿意包含测试结果.

Update: R2018b and R2019a: No change

No significant changes. I'm not bothering to include the test results.

我已将这些基准测试的源代码放在MIT许可下发布的GitHub上. https://github.com/apjanke/matlab-bench

I've put the source code for these benchmarks up on GitHub, released under the MIT License. https://github.com/apjanke/matlab-bench

这篇关于是MATLAB OOP运行缓慢还是我做错了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆