在MATLAB中预分配数组的替代方法是什么?

时间:2010-07-14 23:04:53

标签: arrays optimization memory matlab

  

可能重复:
  Growable data structure in MATLAB

所以在我目前的MATLAB脚本中,我有一个非常大的不确定大小的增长数组。目前我无能为力,因为如果我实际预先分配,它将需要比它应该需要的内存多很多倍的内存(最大可能的值是每像素640,但通常它是2的线上的东西5)。

通常在这种情况下,我会使用C ++中的向量或其他东西,它在给定容量方面呈指数级增长。但我认为Matlab中的矩阵开始分片的速度比目的驱动的C ++向量要快得多。

你们认为这是什么样的最佳选择?或者我应该坚持使用普通数组,并希望顺序添加大约100k元素会起作用吗?

提前致谢。

4 个答案:

答案 0 :(得分:10)

增加数组通常是件坏事,至少如果你多次这样做的话。可以使用一些技巧。 (我已经尝试过所有这些,并且如果你愿意,可以自动为你编写工具。)

问题是与阵列增长相关的时间是O(N ^ 2)操作。如果您需要稍后创建一个大型数组,它也会使您的内存碎片化,从而使您遇到潜在问题。

一种方法是将数组预分配到某个固定大小,然后在超出数组大小时附加大块零。现在使用矩阵索引将新值插入现有数组中。我们的想法是在需要重新分配数组时将其大小加倍。这只会导致一些重新分配,但最终会附加很多你不需要填充的零。最后,您将转储无关和未使用的数组元素。

第二个技巧是使用单元阵列。每次要添加新元素或其块时,只需添加包含这些元素的新单元格。然后在最后,你做一个cell2mat操作。单元技巧的一个问题是它仍然是一个O(N ^ 2)操作,因为单元数组本身本质上是一个指针数组,并且当你追加新元素时必须重新分配指针列表。

你可以做我喜欢的组合技巧,但它需要一个工具来实现操作。我们的想法是创建包含10000个元素块的单元格。用零填充第一个块。然后使用矩阵索引在生成元素时插入新元素。矩阵索引当然很快。当您在该单元格中用完房间时,会附加一个新的大单元格。然后在最后,您将所有单元格连接在一起,小心地丢弃未使用的元素。

最后一个方案涉及相对较少的新细胞元素,如果可能有数百万个追加步骤,那么整体效率最高。

您可以找到我多年来发布的多个文件交换提交中体现的每种方法。第一个可以找到grow_array,它在内部管理附加步骤,担心矩阵索引。

后来,我使用函数句柄或持久变量构建了最后一个方案来维护存储的信息。包含这些实现的工具位于growdata and growdata2

答案 1 :(得分:6)

您可以尝试重新分配元素时std::vector所做的事情 - 每次填充时其容量加倍,其累计成本为 O(1)

测试:

function test_MAT_vector

    LIM = 5e4;
    %%# Normal
    tic;
    A = zeros(1,1);
    for i = 1:LIM
        A(end+1) = i;
    end
    toc

    %%# std::vector clone
    tic;
    B = zeros(1,1);
    for i = 1:LIM
        if(i > numel(B))
            B = [B;zeros(numel(B),1)];
        end
        B(i) = i;
    end
    toc

end

输出:

  

经过的时间是3.489820秒。

     

经过的时间是0.006710秒。

使用单元格

%%# cell
tic;
A = cell(1,1);
for i = 1:LIM
    A(end+1) = {i};
end
toc
  

经过的时间是2.740792秒。

答案 2 :(得分:2)

在上一个问题中,我已将a similar solution发布到@Jacob提议的内容中。

我后来比较了大多数可用选项的性能(上面的@woodchips非常好地总结了)。以下是我在机器上得到的结果:

NUM = 50000;

%%# ========== MINE: ~0.07sec ==========
tic
BLOCK_SIZE = 2000;                           %# initial & increment size
listSize = BLOCK_SIZE;                       %# current list capacity
list = zeros(listSize, 2);                   %# actual list
listPtr = 1;                                 %# pointer to last free position
for i=1:NUM
    %# push items on list
    list(listPtr,:) = [rand rand];           %# store new item
    listPtr = listPtr + 1;                   %# increment position pointer
    %# add new block of memory if needed
    if( listPtr+(BLOCK_SIZE/10) > listSize ) %# less than 10%*BLOCK_SIZE free
        listSize = listSize + BLOCK_SIZE;    %# add new BLOCK_SIZE slots
        list(listPtr+1:listSize,:) = 0;
    end
end
list(listPtr:end,:) = [];                    %# remove unused slots
toc

%%# ========== PREALLOCATION (matrix): ~0.05sec ==========
tic
list = zeros(NUM,2);
for i=1:NUM
    list(i,:) = [rand rand];
end
toc

%%# ========== PREALLOCATION (cell): ~1.1sec ==========
tic
list = cell(NUM,1);
for i=1:NUM
    list{i} = [rand rand];
end
list = vertcat( list{:} );
toc

%%# ============ NO-PREALLOCATION (grow cell): ~5sec ========
tic
list = {};
for i=1:NUM
    list{i} = [rand rand];
end
list = vertcat( list{:} );
toc

%%# ============ NO-PREALLOCATION (grow matrix): ~24sec ========
tic
list = [];
for i=1:NUM
    list(i,:) = [rand rand];
end
toc

%%# ========== GROWDATA (by John D'Errico): ~3.3sec =========
tic
growdata                             %# The initialization call
for i = 1:NUM
    growdata( [rand rand] )          %# push items
end
list = growdata;                     %# unpacking step
toc

答案 3 :(得分:0)

你可以增长一个比数组便宜得多的单元格数组,然后使用cell2mat转换为数组(需要两倍的内存,但如果你拥有它可能是最简单的方法)。

如果您知道可以预先分配正确大小的单元格数组的像素数,请使用实际值数组填充每个单元格(最多640个但通常为2-5个),然后使用cell2mat进行转换它是一个连续的阵列。

如果你担心碎片,你可以在加载应该对你的内存进行碎片整理的单元格之前pack进行碎片整理(将所有内容写入磁盘并将其连续重新加载),然后再进行cell2mat转换。