我编写了以下类来在 matlab 中实现“渐进式”单元格数组:
classdef growinglist < handle
properties (GetAccess='private',SetAccess='private')
inner_cells % inner pre-allocated cell array
end
properties (GetAccess='public',SetAccess='private')
n_elements % current number of elements (index of last valid element in inner_cells)
end
methods
%% constructor
function self=growinglist(varargin)
% you can pass the initial capacity of inner_cells to constructor
if nargin == 1
self.inner_cells =cell(ceil(varargin{1}),1);
else
self.inner_cells =cell(4,1); % initial size is 4
end
self.n_elements = 0;
end
function add(self, element)
% inner_cells is not enough, double it before adding
if self.n_elements + 1 > size(self.inner_cells,1)
n = floor(size(self.inner_cells,1) * 2) - size(self.inner_cells,1) + 1;
self.inner_cells = [self.inner_cells; cell(n,1)];
end
self.n_elements = self.n_elements + 1;
self.inner_cells{self.n_elements} = element;
end
function elements = get_elements(self)
elements = self.inner_cells(1:self.n_elements,1);
end
end
end
但是,它似乎并不像预期的那样快。
事实上,执行这些测试:
n = 30000;
%%%%%% concat everytime
tic
lst = {};
for i=1:n
lst = [lst; 1:10];
end
disp('1 - concat everytime');
toc
%%%%%% exact pre-allocation
tic
lst = cell(n,1);
for i=1:n
lst{i} = 1:10;
end
disp('2 - exact pre-allocation');
toc
%%%%%% "progressive" pre-allocation
tic
inner_cells = cell(4,1);
n_elements = 0;
for i=1:n
if n_elements + 1 > size(inner_cells,1)
n1 = floor(size(inner_cells,1) * 2) - size(inner_cells,1) + 1;
inner_cells = [inner_cells; cell(n1,1)];
end
n_elements = n_elements+1;
inner_cells{n_elements} = 1:10;
end
elements = inner_cells(1:n_elements,1);
disp('3 - "progressive" pre-allocation');
toc
%%%%%% using growing list class
tic
glst = growinglist();
for i=1:n
glst.add(1:10);
end
elements = glst.get_elements();
disp('4 - using growing list class');
toc
%%%%%% using growing list class with exact allocation
tic
glst = growinglist(n);
for i=1:n
glst.add(1:10);
end
elements = glst.get_elements();
disp('5 - use growing list class with exact allocation');
toc
我得到以下结果:
1 - concat everytime
Elapsed time is 4.954054 seconds.
2 - exact pre-allocation
Elapsed time is 0.006791 seconds.
3 - "progressive" pre-allocation
Elapsed time is 0.099431 seconds.
4 - using growing list class
Elapsed time is 11.618202 seconds.
5 - use growing list class with exact allocation
Elapsed time is 12.774726 seconds.
实际上,我预计测试 n.4 和 n.5 的经过时间更接近测试 n.3。但它们甚至比测试 n.1 还要慢(我预计会是最差的)。此外,奇怪的是,测试 n.5 比 n.4 慢。
也许每次设置或执行其他一些副本时都会复制 inner_cells 数组,但我不明白为什么,因为我从句柄类派生了我的类以支持可变性。
我在matlab中很新,所以可能我错过了一些重要的东西......
有什么见解吗?
提前致谢。
PS
我正在使用MATLAB 2011a。
编辑 :
正如@Edric 所建议的,我使用分析器找到了瓶颈,我发现缓慢的罪魁祸首是size(self.inner_cells,1)
方法内部调用的函数Add()
(不知道为什么)。
以这种方式修改类(以减少 size() 调用):
classdef growinglist < handle
properties (GetAccess='private',SetAccess='private')
inner_cells
inner_cells_size
end
properties (GetAccess='public',SetAccess='private')
n_elements % current number of elements (index of last valid element in inner_cells)
end
methods
% constructor
function self=growinglist(varargin)
% you can pass the initial capacity of inner_cells to constructor
if nargin == 1
self.inner_cells =cell(ceil(varargin{1}),1);
self.inner_cells_size = ceil(varargin{1});
else
self.inner_cells =cell(4,1); % initial size is 4
self.inner_cells_size = 4;
end
self.n_elements = 0;
end
function add(self, element)
% inner_cells is not enough, double it before adding
if self.n_elements + 1 > self.inner_cells_size
n = floor(size(self.inner_cells,1) * 2) - size(self.inner_cells,1) + 1;
self.inner_cells = [self.inner_cells; cell(n,1)];
self.inner_cells_size = self.inner_cells_size + n;
end
self.n_elements = self.n_elements + 1;
self.inner_cells{self.n_elements} = element;
end
function elements = get_elements(self)
elements = self.inner_cells(1:self.n_elements,1);
end
end
end
测试现在产生:
1 - concat everytime
Elapsed time is 6.825776 seconds.
2 - exact pre-allocation
Elapsed time is 0.011783 seconds.
3 - "progressive" pre-allocation
Elapsed time is 0.088668 seconds.
4 - using growing list class
Elapsed time is 0.841975 seconds.
5 - use growing list class with exact allocation
Elapsed time is 0.818212 seconds.
这更有意义。