Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why mutating cell array in a class property is so slow?

For example, I have the following class:

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

And the following example loop, where I compare performance with a plain cell array:

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

The output is:

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

As you can see, plain cell array exhibits "linear" performance (which is to be expected), but array wrapped in a class gives horrible quadratic performance.

I tested this on Matlab 2008a and Matlab 2010a.

What causes this? And how can I work around it?

like image 874
Rogach Avatar asked Dec 20 '25 16:12

Rogach


1 Answers

The true power of Matlab is only available to those who know what to avoid :)

One major issue with OOP in interpreted languages (Matlab is not alone in this) is the rather spectacular overhead involved with calling methods, as you have noticed. OOP requires completely different design strategies in Matlab, Python, etc. than in C++, Fortran, etc.

Anyway, you had best avoid calling methods so often, and vectorize as much as you can.

Compare this:

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

where the relevant method in testclass is modified to handle vectorized as well as looped assignments:

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

Results:

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
like image 183
Rody Oldenhuis Avatar answered Dec 23 '25 08:12

Rody Oldenhuis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!