8

Matlab スクリプトに、大規模な非スパース配列に作用する複雑なコード ブロックがあります。コードは、読み取り操作だけでなく、配列内のランダムな要素に対して多くの書き込み操作を実行します。同一のコードは、異なる (大きな) 配列 (つまり、異なる配列変数名を除いて同じコード ブロック) に対して実行する必要があります。

配列名だけが異なる、長くて重複したコード ブロックは必要ありません。

残念ながら、操作を実行する関数を作成して、コード ブロックが 1 回だけ表示されるようにすると、パフォーマンスが 10 倍以上低下します (おそらく配列のコピーが原因です)。ただし、配列をコピーする必要はありません。関数呼び出しの目的は、コードブロックの重複を避けるためだけになるように、「参照渡し」を好むでしょう。ただし、コピーオンライトのセマンティクスを回避する方法はないようです。

また、スクリプトには呼び出し元のスクリプトと同じ変数名が含まれている必要があるため、これを実現するためにスクリプト (関数ではない) を作成することは (私が理解している限り) 不可能です。そのため、配列ごとに異なるスクリプトが必要になります。何も得られないスクリプトを実行したいと思います (それでもコード ブロックが重複します)。

目的の配列変数名を「置き換える」エイリアス変数名を作成することを検討しました。その場合、スクリプトを呼び出してコードの重複を避けることができます。ただし、Matlab でエイリアスを作成する方法が見つかりません。

最後に、関数を利用する関数を作成し、配列変数の文字列evalin()名をこの関数に渡すことを試みましたが、これは機能しますが、パフォーマンスも大幅に低下します-配列を値で関数に渡すのとほぼ同じです(少なくとも 10 倍の性能低下)。

私は、Matlab がコード ブロックの重複を回避する可能な手法を使用して提示する恐ろしいオーバーヘッドを回避するために、非スパース配列で複雑な操作を実行するときにコード ブロックの重複を回避することは不可能であるという結論に達しています。

これは信じがたいことですが、それを回避する方法が見つかりません。

Matlab で複数の非スパース配列に対して同一の複雑な操作を実行するときに、コード ブロックの重複を回避する方法を知っている人はいますか?

4

5 に答える 5

9

Lorenがブログで指摘しているように、MATLABは行列のインライン演算をサポートしています。これは、基本的に、参照による配列の受け渡し、関数内での配列の変更、および結果の返しを対象としています。あなたはそれを知っているようですが、スクリプトには呼び出し元のスクリプトと同じ変数名が含まれている必要があるため、誤って述べています。これが間違っていることを示すコード例を次に示します。テストするときは、そのままコピーして関数として保存してください。

function inplace_test
y = zeros(1,1e8);
x = zeros(1,1e8);

tic; x = compute(x); toc
tic; y = compute(y); toc
tic; x = computeIP(x); toc
tic; y = computeIP(y); toc
tic; x = x+1; toc
end

function x=computeIP(x)
x = x+1;
end

function y=compute(x)
y = x+1;
end

私のコンピューターでの時間結果:

Elapsed time is 0.243335 seconds.
Elapsed time is 0.251495 seconds.
Elapsed time is 0.090949 seconds.
Elapsed time is 0.088894 seconds.
Elapsed time is 0.090638 seconds.

ご覧のとおり、インプレース関数を使用する最後の2つの呼び出しは、入力配列xとの両方で同等に高速ですyx = x+1また、関数なしで実行するのと同じくらい高速です。唯一重要なことは、関数内の入力パラメーターと出力パラメーターが同じであるということです。そしてもう1つあります...

コードの何が問題になっているのかを推測する必要がある場合は、インプレースであると予想されるネストされた関数を作成したと思います。そして、そうではありません。したがって、以下のコードは機能しません。

function inplace_test
y = zeros(1,1e8);
x = zeros(1,1e8);

tic; x = compute(x); toc
tic; y = compute(y); toc
tic; x = computeIP(x); toc
tic; y = computeIP(y); toc
tic; x = x+1; toc

    function x=computeIP(x)
        x = x+1;
    end

    function y=compute(x)
        y = x+1;
    end
end

Elapsed time is 0.247798 seconds.
Elapsed time is 0.257521 seconds.
Elapsed time is 0.229774 seconds.
Elapsed time is 0.237215 seconds.
Elapsed time is 0.090446 seconds.

結論-これらの入れ子関数には注意してください。

于 2012-10-26T06:14:02.777 に答える
4

名前で参照する代わりに、すべての配列を 1 つの cell 配列に入れ、インデックスを使用することができます。関数は引き続き配列をコピーしますが、スクリプトはジョブを実行できます。

于 2012-10-25T23:42:30.963 に答える
2

別の答え:

In-place Operations on Dataという優れた記事があります。どうやら、2 つの落とし穴があるかもしれません。

  1. (これは些細なことであり、おそらく実行したことでしょう) 関数の定義だけでなく、それを呼び出す場所でも同じ in 変数と out 変数名を使用する必要があります。
  2. これは、コマンドラインからではなく、ANOTHER FUNCTION から関数を呼び出した場合にのみ機能します。奇妙な...私が試したところ、オーバーヘッドはありますが、非常に小さいです (10000 x 10000 配列の場合、コマンド ラインから 1 秒、別の関数から 0.000361 秒でした)。

これがうまくいかない場合は、C++ MEX ファイルでインプレース操作を実行できるドキュメント化されていない機能を使用できます。これは厄介ですが、これに関する記事は次のとおりです。Matlab mex in-placeediting

于 2012-10-26T01:08:02.297 に答える
2

ブライアン L によって提案されたハンドル ソリューションは機能しますが、ラップされたデータを変更する最初の呼び出しには時間がかかります (元のデータのコピーを作成する必要があるため)。

これを試して:

SomeData.m

classdef SomeData < handle
    properties        
            X
    end
    methods                
        function obj = SomeData(x)            
            if nargin > 0
                obj.X = x;
            else
                obj.X = [];
            end
        end
    end
end

LargeOp.m

function directArray = LargeOp( someData, directArray )
    if nargin > 1
        directArray(1,1) = rand(1);
    else
        someData.X(1,1) = rand(1);
        directArray = [];    
    end
end

パフォーマンスをテストするスクリプト

large = zeros(10000,10000);

data = SomeData(large);

tic
LargeOp(data);
toc

tic
large = LargeOp(data,large);
toc

tic
LargeOp(data);
toc

tic
large = LargeOp(data,large);
toc

結果

Elapsed time is 0.364589 seconds.
Elapsed time is 0.450668 seconds.
Elapsed time is 0.001073 seconds.
Elapsed time is 0.443150 seconds.
于 2012-10-26T01:27:27.643 に答える
1

必要に応じて、入れ子関数を作成することでこれを実現できます。

function A = evensarenegative(n)
    A = zeros(n,1);

    for i = 1:n
        if mod(i,2)
            nested1(i)
        else
            nested2(i)
        end
    end

    function nested1(i)
        A(i) = i;
    end

    function nested2(i)
        A(i) = -i;
    end
end

ここでは、関数は同じワークスペース、特にAマトリックスを共有しているため、変数はコピーされません。特に、より大きなワークフローの一部としてマイナーな(しかしおそらく冗長な)操作がたくさんある場合は、コードを整理するのに便利な方法だと思います。

于 2012-10-25T23:14:54.417 に答える