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

如您所见,普通单元阵列表现出“线性”性能(这是意料之中的),但包裹在一个类中的数组给出了可怕的二次性能。

我在 Matlab 2008a 和 Matlab 2010a 上对此进行了测试。

这是什么原因造成的?我该如何解决它?

4

2 回答 2

3

Matlab 的真正威力只适用于那些知道要避免什么的人:)

正如您所注意到的,解释语言中 OOP 的一个主要问题(Matlab 并不孤单)是调用方法所涉及的相当惊人的开销。OOP 需要在 Matlab、Python 等中与在 C++、Fortran 等中完全不同的设计策略。

无论如何,您最好避免频繁调用方法,并尽可能多地向量化。

比较一下:

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 回答