私は [〜#〜] matlab [〜#〜][〜#〜] oop [〜#〜] を試しています。最初はC++のLoggerを模倣しましたクラスと私はすべての文字列ヘルパー関数を文字列クラスに入れています。a.find( b )
、strcat( a b )
の代わりにa + b
、a == b
、strcmp( a, b )
のようなことができると素晴らしいと思い、最初の要素を取得しますstrfind( a, b )
などの.
問題:スローダウン
上記のものを使用すると、劇的なスローダウンにすぐに気付きました。私はそれを間違っていますか(MATLABの経験がかなり限られているので確かに可能です)、またはMATLABのOOPは単に多くのオーバーヘッドを導入しますか?
私のテストケース
文字列に対して行った簡単なテストは次のとおりです。基本的には文字列を追加し、追加した部分を再度削除します。
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回の反復の合計時間(秒):
btest 0.550(String.SetLength 0.138、String.plus 0.065、String.Length 0.057を使用)
atest 0.015
ロガーシステムの結果も同様です:Stringクラスを内部で使用する場合、frpintf( 1, 'test\n' )
の1000回の呼び出しで0.1秒、システムの1000回の呼び出しで7(!)秒(OK、より多くのロジックがありますが、C++と比較するには:出力側でstd::string( "blah" )
とstd::cout
を使用する私のシステムのオーバーヘッドと通常のstd::cout << "blah"
は1ミリ秒のオーダーです。
クラス/パッケージ関数を検索するときのオーバーヘッドですか?
MATLABは解釈されるため、実行時に関数/オブジェクトの定義を検索する必要があります。そのため、クラスまたはパッケージの関数とパスにある関数の検索に、はるかに多くのオーバーヘッドが関係しているのではないかと思っていました。私はこれをテストしようとしましたが、見知らぬ人がいます。クラス/オブジェクトの影響を排除するために、パス内の関数とパッケージ内の関数の呼び出しを比較しました。
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
上記と同じ方法で収集された結果:
atest 0.004秒、ctestで0.001秒
btest 0.060秒、util.ctestで0.014秒
したがって、このオーバーヘッドはすべて、MATLABのOOP実装の定義の検索に時間を費やしていますが、このオーバーヘッドはパスに直接ある関数にはありませんか?
私は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 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 classdef nop(obj)(mファイル):0.90560秒9.06コールごとのusec classdef class.staticnop():1.16361秒11.64 usec per call 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までの同様の結果。これは、Windows XP x64 32ビットMATLABを実行している場合です。
「Java nop()」は、Mコードループ内から呼び出されるdo-nothing Javaメソッドであり、各呼び出しでのMATLABからJavaへのディスパッチオーバーヘッドを含みます。 )from Java」は、Java for()ループで呼び出されるものと同じであり、その境界ペナルティは発生しません。JavaおよびCタイミングを洗練されたコンパイラーは、呼び出しを完全に最適化できます。
パッケージスコープメカニズムは新しく、classdefクラスとほぼ同時に導入されました。その動作は関連している可能性があります。
いくつかの暫定的な結論:
obj.nop()
構文は、classdefオブジェクトの同じメソッドであっても、nop(obj)
構文よりも低速です。 Javaオブジェクト(図示せず)についても同じです。高速にしたい場合は、nop(obj)
を呼び出します。これがなぜそうなのかを言うのは、私の側の憶測にすぎない。 MATLABエンジンのOO内部は公開されていません。それ自体は解釈された問題とコンパイルされた問題ではありません-MATLABにはJITがあります-しかし、MATLABの緩やかな入力と構文は実行時により多くの作業を意味する場合があります(例: 「f(x)」が関数呼び出しであるか、配列へのインデックスであるかを構文だけから判断することはできません。これは、実行時のワークスペースの状態に依存します。)MATLABのクラス定義がファイルシステムに関連付けられている可能性があります他の多くの言語がそうでないように述べてください。
じゃあ何をすればいいの?
これに対する慣用的なMATLABアプローチは、オブジェクトインスタンスが配列をラップするようにクラス定義を構造化することにより、コードを「ベクトル化」することです。つまり、各フィールドは並列配列を保持します(MATLABドキュメンテーションでは「平面」構成と呼ばれます)。それぞれがスカラー値を保持するフィールドを持つオブジェクトの配列を持つのではなく、それ自体が配列であるオブジェクトを定義し、メソッドが配列を入力として受け取り、フィールドと入力に対してベクトル化された呼び出しを行うこれにより、実行されるメソッド呼び出しの数が減ります。これは、ディスパッチのオーバーヘッドがボトルネックにならないようにすることを願っています。
C++またはJavaクラスを模倣することはおそらく最適ではありません。Java/ C++クラスは通常、オブジェクトができる限り具体的な(つまり、多くのさまざまなクラスの)、それらを配列、コレクションオブジェクトなどで構成し、ループで繰り返します。高速のMATLABクラスを作成するには、そのアプローチを裏返します。フィールドが配列である大きなクラスを持ち、それらでベクトル化されたメソッドを呼び出します配列。
要点は、言語の長所(配列処理、ベクトル化された数学)に合わせてコードを調整し、弱点を回避することです。
編集:元の投稿以来、R2010bとR2011aが出てきました。全体像は同じで、MCOS呼び出しは少し速くなり、Javaと古いスタイルのメソッド呼び出しはslower。
編集:私はここで「パスの感度」に関するいくつかのメモを関数呼び出しタイミングの追加テーブルと共に持っていました。関数時間はMatlabパスの設定方法によって影響されましたが、それは私の特定のネットワーク設定の異常でした時間。上記のチャートは、時間の経過に伴う私のテストの優勢の典型的な時間を反映しています。
編集(2012年2月13日):R2011bがリリースされ、パフォーマンスの状況はこれを更新するのに十分に変更されました。
アーチ:PCWINリリース:2011b マシン:R2011b、Windows XP、8 x Core i7-2600 @ 3.40GHz、3 GB RAM、NVIDIA NVS 300 各操作を100000回実行 スタイルコールごとの合計マイクロ秒 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()private関数: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プロパティw i番目のゲッター:1.39912 13.99 + pkg.nop()関数:0.87345 8.73 + pkg.nop()内部から+ 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からのJava obj.nop():0.00010 0.00 MEX mexnop():0.08709 0.87 C nop():0.00001 0.00 j()(組み込み):0.00251 0.03
これの結果は次のとおりだと思います:
foo(obj)
構文を使用する限り、コストは古いスタイルクラスと同等になりました。そのため、ほとんどの場合、メソッドの速度が古いスタイルのクラスに固執する理由ではなくなりました。 (賞賛、MathWorks!)ベンチマークコードを再構築し、R2014aで実行しました。
Matlab R2014a on PCWIN64 Matlab 8.3.0.532(R2014a)/ Java 1.7.0_11 on PCWIN64 Windows 7 6.1(eilonwy-win7) マシン: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 nop()@classのプライベート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.nop()内部から+ 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 builtin j():0.02 struct s.fooフィールドアクセス:0.14 isempty(persistent):0.00
@Shakedから親切に提供されたR2015bの結果を次に示します。これは、bigの変更です:OOPは大幅に高速になり、現在obj.method()
構文はmethod(obj)
と同じくらい高速で、従来のOOPオブジェクトよりもはるかに高速です。
Matlab R2015b on PCWIN Matlab 8.6.0.267246(R2015b)/ Java 1.7.0_60 on PCWIN64 Windows 8 6.2(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 nop()@classのプライベート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.no内部からのp()+ 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():0.01 MEX mexnop():0.33 builtin j():0.15 struct s.fooフィールドアクセス: 0.25 isempty(永続):0.13
R2018aの結果は次のとおりです。新しい実行エンジンがR2015bに導入されたときに見たような大きなジャンプではありませんが、それでも、前年比でかなりの改善が見られます。特に、匿名関数ハンドルはずっと高速になりました。
Matlab R2018a on MACI64 Matlab 9.4.0.813654(R2018a)/ Java 1.8.0_144 on MACI64 Mac OS X 10.13.5(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 builtin j():0.10 struct s.fooフィールドアクセス:0.16 isempty(persistent):0.07
大きな変更はありません。テスト結果を含めることを気にしません。
MIT License。 https://github.com/apjanke/matlab-bench の下でリリースされたGitHubにこれらのベンチマークのソースコードを掲載しました。
ハンドルクラスには、クリーンアップのためにそれ自体へのすべての参照を追跡することによる追加のオーバーヘッドがあります。
ハンドルクラスを使用せずに同じ実験を試して、結果を確認してください。
実際にはコードには問題ありませんが、Matlabには問題があります。私はそれのように見えるように遊んでいるようなものだと思います。クラスコードをコンパイルするのはオーバーヘッドに他なりません。単純なクラスポイント(一度ハンドルとして)ともう一方(値クラスとして1回)でテストを行いました
classdef Pointh < handle
properties
X
Y
end
methods
function p = Pointh (x,y)
p.X = x;
p.Y = y;
end
function d = dist(p,p1)
d = (p.X - p1.X)^2 + (p.Y - p1.Y)^2 ;
end
end
end
ここにテストがあります
%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代わりに構造体が変数をグループ化するための適切な選択です
OOのパフォーマンスは、使用するMATLABバージョンに大きく依存します。すべてのバージョンについてコメントすることはできませんが、経験から、2012aは2010バージョンよりもはるかに改善されていることがわかります。ベンチマークがないため、表示する数値はありません。ハンドルクラスを使用して排他的に記述され、2012aで記述されたコードは、以前のバージョンではまったく実行されません。