Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pre-allocating memory in MATLAB à la std::vector::reserve(n)

So reserve is quite useful when you have a rough idea of your size requirements. Does anyone know of a similar method to pre-allocate arrays in MATLAB?

I'm not really interested in hacky (but effective) methods like the following:

x = zeros(1000,1);
for i = 1:10000
    if i > numel(x)
       x = [x;zeros(size(x))];
    end
    x(i) = rand;
end
x(i+1:end) = [];
like image 261
Jacob Avatar asked Oct 09 '22 16:10

Jacob


2 Answers

The "hacky" way is the only way to do it. However, you do not need to check i <= numel(x). The array will be expanded automatically (but without array doubling):

x = zeros(1000,1);
for i = 1:10000
    x(i) = rand;
end
x(i+1:end) = [];

EDIT: To keep it simple while still retaining the array doubling, you can write a class, or simply a few helper functions (below).

EDIT2: The usage of helper functions will slow things down compared to the manual hack. In MATLAB 2010 it is still much faster than naive growth. In MATLAB 2011 the naive approach is actually faster, suggesting that this version has smarter allocation. Perhaps it is fast enough so that no hack is needed at all. Thanks to Andrew Janke for pointing this out.

function listtest()
    n = 10000;
    l = new_list();
    for i=1:n
        l = list_append(l, i);
    end
    a = list_to_array(l);
end

function l = new_list()
    l = [0 0];
end
function l = list_append(l, e)
    if l(1)+1 == length(l)
        l(length(l)*2) = 0;
    end
    l(1) = l(1)+1;
    l(l(1)+1) = e;
end
function a = list_to_array(l)
    a = l(2:1+l(1));
end

EDIT (from AndrewJanke)

Here's code to compare the speed of the implementations.

function manual_reserve_example(n)
x = zeros(1000,1);
for i = 1:n
    if i > numel(x)
       x = [x;zeros(size(x))];
    end
    x(i) = i;
end
x(i+1:end) = [];
end

function naive_growth(n)
x = 0;
for i = 1:n
    x(i) = i;
end
end

function compare_them(n)
fprintf('Doing %d elements in Matlab R%s\n', n, version('-release'));
tic;
naive_growth(n);
fprintf('%30s  %.6f sec\n', 'naive_growth', toc);
tic;
manual_reserve_example(n);
fprintf('%30s  %.6f sec\n', 'manual_reserve', toc);
tic;
listtest(n);
fprintf('%30s  %.6f sec\n', 'listtest', toc);
end
like image 92
rasmus Avatar answered Oct 12 '22 02:10

rasmus


The cleanest solution to the example that you provided is to iterate backwards.

for i = 10000:-1:1
    x(i) = rand;
end

This does not work in cases where the end size is actually unknown, but it has come in handy for me more often than I would have expected.


Otherwise I usually implement a "double on overflow" algorithm like you show in the original question.

The clean solution is to wrap a Matlab class around a respectible vector resize algorithm, and then use that class. I am not aware of any reason such a class could not be built, but I've never actually sat down and tried to implement one. (I'm curious if an example already exists on the file exchange somewhere.)

like image 45
Pursuit Avatar answered Oct 12 '22 02:10

Pursuit