为什么在类属性中改变单元格数组是如此之慢?

时间:2012-11-05 17:37:21

标签: performance matlab

例如,我有以下课程:

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上进行了测试。

是什么原因引起的?我该如何解决它?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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