3

たとえば、次のクラスがあります。

classdef testclass < handle
    properties
            buckets
    end
    methods
        function tc = testclass(sz)
            tc.buckets = cell(1, sz);
        end
        function put(tc,k)
            tc.buckets{k}{1} = 1;
        end
    end
end

そして、次のループの例では、パフォーマンスをプレーンセル配列と比較しています。

tm = @java.lang.System.currentTimeMillis;

for N=[100 200 400 1000 2000 4000 8000]
    tc = testclass(N);
    Tstart = tm();
    for k=1:N
        tc.put(k);
    end
    Tend = tm();
    fprintf(1, 'filling hash class (size %d): %d ms\n', N, Tend - Tstart);

    arr = cell(1,N);
    Tstart = tm();
    for k=1:N
        arr{k}{1} = 1;
    end
    Tend = tm();
    fprintf(1, 'filling cell array (size %d): %d ms\n', N, Tend - Tstart);
end

出力は次のとおりです。

filling hash class (size 100): 8 ms
filling cell array (size 100): 0 ms
filling hash class (size 200): 9 ms
filling cell array (size 200): 0 ms
filling hash class (size 400): 24 ms
filling cell array (size 400): 1 ms
filling hash class (size 1000): 108 ms
filling cell array (size 1000): 2 ms
filling hash class (size 2000): 370 ms
filling cell array (size 2000): 5 ms
filling hash class (size 4000): 1396 ms
filling cell array (size 4000): 10 ms
filling hash class (size 8000): 5961 ms
filling cell array (size 8000): 21 ms

ご覧のとおり、プレーンセル配列は「線形」パフォーマンスを示しますが(これは予想されることです)、クラスにラップされた配列は恐ろしい2次パフォーマンスを提供します。

これをMatlab2008aとMatlab2010aでテストしました。

これの原因は何ですか?そして、どうすればそれを回避できますか?

4

2 に答える 2

3

Matlabの真の力は、避けるべきことを知っている人だけが利用できます:)

インタプリタ言語でのOOPに関する1つの主要な問題(Matlabだけではありません)は、お気づきのように、メソッドの呼び出しに伴うかなり壮大なオーバーヘッドです。OOPには、C ++、Fortranなどとはまったく異なるMatlab、Pythonなどの設計戦略が必要です。

とにかく、メソッドを頻繁に呼び出すことは避け、できるだけベクトル化するのが最善です。

これを比較してください:

clc, clear classes    
feature accel off  % to make sure JIT doesn't throw things off

tm = @java.lang.System.currentTimeMillis;

for N = [100 200 400 1000 2000 4000 8000]

    tc = testclass(N);

    % call method inside loop
    Tstart = tm();
    for k=1:N
        tc.put(k);
    end
    Tend = tm();
    fprintf(1, 'filling hash class (loop, size %d)      : %d ms\n', N, Tend - Tstart);

    % call method only once
    Tstart = tm();
    tc.put(1:N);    
    Tend = tm();
    fprintf(1, 'filling hash class (vectorized, size %d): %d ms\n', N, Tend - Tstart);

    % cell-array direct assignment, looped version
    arr = cell(1,N);
    Tstart = tm();
    for k=1:N
        arr{k}{1} = 1;
    end
    Tend = tm();
    fprintf(1, 'filling cell array (loop, size %d)      : %d ms\n', N, Tend - Tstart);

    % cell-array direct assignment, vectorized version
    arr = cell(1,N);
    Tstart = tm();
    [arr{:}] = deal({1});
    Tend = tm();
    fprintf(1, 'filling cell array (vectorized, size %d): %d ms\n\n', N, Tend - Tstart);
end

ここで、の関連するメソッドは、testclassベクトル化された割り当てとループされた割り当てを処理するように変更されます。

classdef testclass < handle
    properties
            buckets
    end
    methods
        function tc = testclass(sz)
            tc.buckets = cell(1, sz);
        end
        function put(tc,k)
            [tc.buckets{k}] = deal({1});
        end
    end
end

結果:

filling hash class (loop, size 100)      : 5 ms
filling hash class (vectorized, size 100): 0 ms
filling cell array (loop, size 100)      : 0 ms
filling cell array (vectorized, size 100): 0 ms

filling hash class (loop, size 200)      : 7 ms
filling hash class (vectorized, size 200): 0 ms
filling cell array (loop, size 200)      : 0 ms
filling cell array (vectorized, size 200): 0 ms

filling hash class (loop, size 400)      : 15 ms
filling hash class (vectorized, size 400): 1 ms
filling cell array (loop, size 400)      : 1 ms
filling cell array (vectorized, size 400): 0 ms

filling hash class (loop, size 1000)      : 36 ms
filling hash class (vectorized, size 1000): 2 ms
filling cell array (loop, size 1000)      : 2 ms
filling cell array (vectorized, size 1000): 1 ms

filling hash class (loop, size 2000)      : 73 ms
filling hash class (vectorized, size 2000): 2 ms
filling cell array (loop, size 2000)      : 4 ms
filling cell array (vectorized, size 2000): 2 ms

filling hash class (loop, size 4000)      : 145 ms
filling hash class (vectorized, size 4000): 5 ms
filling cell array (loop, size 4000)      : 9 ms
filling cell array (vectorized, size 4000): 4 ms

filling hash class (loop, size 8000)      : 292 ms
filling hash class (vectorized, size 8000): 8 ms
filling cell array (loop, size 8000)      : 18 ms
filling cell array (vectorized, size 8000): 9 ms
于 2012-11-05T20:21:37.603 に答える
0

これの原因は何ですか?そして、どうすればそれを回避できますか?

速度低下は、オブジェクトを使用した結果です。オブジェクトはプログラマーとして多くのことを購入しますが、オーバーヘッドが伴います。オブジェクトを放棄せずにこのオーバーヘッドを回避することはできません。言語と特定の実装に応じて、オーバーヘッドは取るに足らないか、かなりのものになります。

メンバー配列とネイキッド配列を使用してオブジェクトを作成する単純なC++プログラムを作成しました。オブジェクト内の配列を埋めるのは、裸の配列を埋めるよりもはるかに時間がかかります。

#include <iostream>
#include <ctime>
 
static const int MAX_SIZE  = 1000000;
 
class Holder{
  public:
    void putValue(int val, int idx);
  private:
        int data[MAX_SIZE];
};
 
void Holder::putValue(int v, int i){
        data[i] = v;
}
 
int main(int argc, char **argv){
 
        Holder h;
        int data[MAX_SIZE];
 
        
        int j, i;
        clock_t begin, end;
        int outerLoop = 200;
 
        fprintf(stderr,"Testing array in object!\n");
        begin = clock();
        for ( j=0; j<outerLoop; j++)
                for (  i = 0; i<MAX_SIZE; i++)
                        h.putValue(i,i);
        end = clock();
 
        fprintf(stderr,"Done! El time:%3.3f\n", double(end - begin)/CLOCKS_PER_SEC);
 
 
        fprintf(stderr,"Testing naked array 2!\n");
        begin = clock();
        for ( j=0; j<outerLoop; j++)
                for ( i = 0; i<MAX_SIZE; i++)
                        data[i] = i;
        end = clock();
        fprintf(stderr,"Done! El time:%3.3f\n", double(end - begin)/CLOCKS_PER_SEC);
 
 
}

出力は次のとおりです。

オブジェクト内の配列をテストしています!

終わり!エルタイム:1.413

ネイキッドアレイ2のテスト!

終わり!エルタイム:0.690

于 2012-11-05T18:21:39.760 に答える