私はしばらくOO MATLABを使用しており、同様のパフォーマンスの問題を調べていました。
簡単に言えば、そうです。MATLABのOOPは少し遅いです。メソッドコールのオーバーヘッドはかなり大きく、主流のOO言語よりも高く、それに対してできることはあまりありません。理由の一部は、慣用的なMATLABが「ベクトル化された」コードを使用してメソッド呼び出しの数を減らし、呼び出しごとのオーバーヘッドが高い優先順位ではないためです。
何もしない「nop」関数をさまざまなタイプの関数およびメソッドとして記述することで、パフォーマンスのベンチマークを行いました。ここにいくつかの典型的な結果があります。
>> call_nops
コンピューター:PCWINリリース:2009b
各関数/メソッドを100000回呼び出す
nop()関数:呼び出しごとに0.02261秒0.23 usec
nop1-5()関数:呼び出しごとに0.02182秒0.22 usec
nop()サブ関数:呼び出しごとに0.02244秒0.22 usec
@()[]匿名関数:呼び出しごとに0.08461秒0.85 usec
nop(obj)メソッド:0.24664秒2.47呼び出しごとのusec
nop1-5(obj)メソッド:呼び出しごとに0.23469秒2.35 usec
nop()プライベート関数:呼び出しごとに0.02197秒0.22 usec
classdef nop(obj):呼び出しあたり0.90547秒9.05 usec
classdef obj.nop():1.75522秒17.55コールごとのusec
classdef private_nop(obj):呼び出しあたり0.84738秒8.47 usec
classdef nop(obj)(m-file):呼び出しあたり0.90560秒9.06 usec
classdef class.staticnop():呼び出しごとに1.16361秒11.64 usec
Java nop():2.43035秒24.30呼び出しごとのusec
Java static_nop():呼び出しあたり0.87682秒8.77 usec
JavaからのJava nop():呼び出しごとに0.00014秒0.00 usec
MEX mexnop():呼び出しあたり0.11409秒1.14 usec
C nop():呼び出しごとに0.00001秒0.00 usec
R2008aからR2009bまでの同様の結果。これは、32ビットのMATLABを実行しているWindows XP x64上にあります。
"Java nop()"は、Mコードループ内から呼び出される何もしないJavaメソッドであり、呼び出しごとにMATLABからJavaへのディスパッチオーバーヘッドが含まれています。「Java nop()from Java」は、Java for()ループで呼び出されるものと同じものであり、その境界ペナルティは発生しません。JavaとCのタイミングを詳細に検討してください。賢いコンパイラーは、呼び出しを完全に最適化することができます。
パッケージスコーピングメカニズムは新しく、classdefクラスとほぼ同時に導入されました。その動作は関連している可能性があります。
いくつかの暫定的な結論:
- メソッドは関数よりも低速です。
- 新しいスタイル(classdef)メソッドは、古いスタイルのメソッドよりも低速です。
- classdefオブジェクトの同じメソッドであって
obj.nop()
も、新しい構文は構文よりも低速ですnop(obj)
。Javaオブジェクトについても同じです(表示されていません)。早く行きたい場合は、に電話してくださいnop(obj)
。
- Windowsの64ビットMATLABでは、メソッド呼び出しのオーバーヘッドが高くなります(約2倍)。(表示されていません。)
- MATLABメソッドのディスパッチは、他のいくつかの言語よりも低速です。
これがなぜそうであるかを言うことは、私の側の推測です。MATLABエンジンのOO内部は公開されていません。それ自体は解釈された問題とコンパイルされた問題ではありません。MATLABにはJITがありますが、MATLABのタイピングと構文が緩いため、実行時の作業が増える可能性があります。(たとえば、「f(x)」が関数呼び出しなのか、配列へのインデックスなのかを構文だけで判断することはできません。実行時のワークスペースの状態によって異なります。)これは、MATLABのクラス定義が関連付けられているためである可能性があります。他の多くの言語にはない方法でファイルシステムの状態に。
じゃあ何をすればいいの?
これに対する慣用的なMATLABのアプローチは、オブジェクトインスタンスが配列をラップするようにクラス定義を構造化することにより、コードを「ベクトル化」することです。つまり、その各フィールドは並列配列を保持します(MATLABドキュメンテーションでは「平面」編成と呼ばれます)。オブジェクトの配列を持つのではなく、それぞれにスカラー値を保持するフィールドがあり、それ自体が配列であるオブジェクトを定義し、メソッドが配列を入力として受け取り、フィールドと入力に対してベクトル化された呼び出しを行います。これにより、ディスパッチのオーバーヘッドがボトルネックにならないように、メソッドの呼び出し回数が減ります。
MATLABでC ++またはJavaクラスを模倣することはおそらく最適ではありません。通常、Java / C ++クラスは、オブジェクトができるだけ具体的に(つまり、多くの異なるクラス)最小のビルディングブロックになるように構築され、配列、コレクションオブジェクトなどでそれらを構成し、ループでそれらを繰り返します。高速なMATLABクラスを作成するには、そのアプローチを裏返しにします。フィールドが配列であるより大きなクラスを持ち、それらの配列に対してベクトル化されたメソッドを呼び出します。
ポイントは、言語の強み(配列処理、ベクトル化された数学)に合わせてコードを調整し、弱点を回避することです。
編集:元の投稿以来、R2010bとR2011aが出てきました。全体像は同じで、MCOS呼び出しは少し速くなり、Javaと古いスタイルのメソッド呼び出しは遅くなります。
編集:私はここで「パスの感度」に関するいくつかのメモを関数呼び出しのタイミングの追加の表とともに示しましたが、関数時間はMatlabパスの構成方法によって影響を受けましたが、それは私の特定のネットワーク設定の異常であるようです時間。上のグラフは、時間の経過に伴う私のテストの典型的な時間を反映しています。
アップデート:R2011b
編集(2012年2月13日):R2011bがリリースされ、パフォーマンスの図はこれを更新するために十分に変更されました。
アーチ: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()、10xループ展開: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
ゲッター付きのclassdefプロパティ:1.39912 13.99
+ pkg.nop()関数:0.87345 8.73
+ pkg内から+ pkg.nop():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からのJava obj.nop():0.00010 0.00
MEX mexnop():0.08709 0.87
C nop():0.00001 0.00
j()(組み込み):0.00251 0.03
これの結果は次のとおりだと思います:
- MCOS / classdefメソッドはより高速です。
foo(obj)
構文を使用する限り、コストは今や古いスタイルのクラスと同等です。そのため、ほとんどの場合、メソッドの速度が古いスタイルのクラスに固執する理由にはなりません。(賞賛、MathWorks!)
- 名前空間に関数を配置すると、処理が遅くなります。(R2011bの新機能ではなく、私のテストの新機能)
更新:R2014a
ベンチマークコードを再構築し、R2014aで実行しました。
PCWIN64上のMatlab R2014a
Matlab 8.3.0.532(R2014a)/ PCWIN64 Windows 7 6.1(eilonwy-win7)上のJava 1.7.0_11
マシン:Core i7-3615QM CPU @ 2.30GHz、4 GB RAM(VMware Virtual Platform)
nIters = 100000
動作時間(µsec)
nop()関数:0.14
nop()サブ関数:0.14
@()[]匿名関数:0.69
nop(obj)メソッド:3.28
@classのnop()プライベートfcn: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
ゲッター付きのclassdefプロパティ:19.26
+ pkg.nop()関数:4.03
+ pkg内から+ pkg.nop():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
組み込みj():0.02
構造体s.fooフィールドアクセス:0.14
isempty(永続的):0.00
更新:R2015b:オブジェクトが速くなりました!
これは、@ Shakedによって提供されたR2015bの結果です。これは大きな変更です。OOPは大幅に高速になり、obj.method()
構文はmethod(obj)
と同じくらい速くなり、レガシーOOPオブジェクトよりもはるかに高速になりました。
PCWIN64上のMatlab R2015b
Matlab 8.6.0.267246(R2015b)/ PCWIN64 Windows 8 6.2上のJava 1.7.0_60(nanit-shaked)
マシン:Core i7-4720HQ CPU @ 2.60GHz、16 GB RAM(20378)
nIters = 100000
動作時間(µsec)
nop()関数:0.04
nop()サブ関数:0.08
@()[]匿名関数:1.83
nop(obj)メソッド:3.15
@classのnop()プライベートfcn: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プロパティ:0.64
+ pkg.nop()関数:0.04
+ pkg.nop()+ 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
struct s.fooフィールドアクセス:0.25
isempty(永続的):0.13
アップデート:R2018a
これがR2018aの結果です。これは、R2015bで新しい実行エンジンが導入されたときに見られた大きなジャンプではありませんが、それでも年々改善されています。特に、無名関数ハンドルはより高速になりました。
MACI64上のMatlab R2018a
MACI64 Mac OS X 10.13.5上のMatlab 9.4.0.813654(R2018a)/ Java 1.8.0_144(eilonwy)
マシン:Core i7-3615QM CPU @ 2.30GHz、16 GB RAM
nIters = 100000
動作時間(µsec)
nop()関数:0.03
nop()サブ関数:0.04
@()[]匿名関数: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定数:0.16
classdefプロパティ:0.13
ゲッター付きのclassdefプロパティ:0.39
+ pkg.nop()関数:0.02
+ pkg.nop()内部から+ 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のJava obj.nop():0.03
MEX mexnop():3.54
組み込みj():0.10
struct s.fooフィールドアクセス:0.16
isempty(永続的):0.07
更新:R2018bおよびR2019a:変更なし
大きな変更はありません。私はテスト結果を含めることを気にしていません。
ベンチマークのソースコード
これらのベンチマークのソースコードは、MITライセンスの下でリリースされたGitHubに公開しました。https://github.com/apjanke/matlab-bench