我拼凑了一个测试函数来推断有关动态分配的一些更具体的细节。使用的函数在答案的底部。
该函数调用多个位置函数,这些函数根据具有不同对数间隔长度的输入 x 计算值 y。功能不同,但循环类型(for 和 while)和分配方案(动态和预分配)。结果来自 R2014b。
四个函数的运行时间结果如下所示。
对于低元素计数,运行时间是恒定的;对于高元素计数,所有四个函数都进入了具有相似增长率的增长状态。
数据的幂拟合(即c = ArgMin[c(1)*n^c(2) - time])返回四个数据集的指数范围为 0.93 - 0.96(线性增长)。
我对这些结果感到非常惊讶,因为要么是我在测试中遗漏了一些东西,要么是 Matlab 的 JIT 编译器非常擅长分配数组(可能是底层的链表)。
预分配的for-loop 确实运行得最快,但只比动态版本快 12%。
谈到内存消耗,我运行了for-loop 变体的动态和预分配版本,用于在一千万个元素范围内的几个线性增加的元素计数。
Matlab 使用的总内存是在循环的开始和结束时采样的。
结果如下所示。
如图所示,函数调用的起始内存在两个函数之间(大部分情况下)是相同的。
预分配版本的内存使用量随元素数量线性增加,这是预期的。
但是用于动态版本的内存,虽然仍然是分段和趋势线性的,但在内存的斜率中在几个点上有跳跃。
对我来说,这意味着 Matlab 正在做某种形式的表增长(不一定像我认为 Python 那样加倍表)而不是每次迭代都重新分配数组(这确实让我质疑我对上述链接列表的想法时间讨论)。
我发现这些结果很有趣,但除了上述想法之外,我无法真正得出更多结论。
但是,无论如何,显式预分配对于任何数字繁重的应用程序中的运行时和代码维护总是更好。
当然,欢迎任何关于结果和测试功能的 cmets(可能讨论这个测试是如何完全错误的)。这就是我(我们)学习的方式。
function [] = test()
% Constants and setup
funs = {@forDynamic,@forAllocate,@whileDynamic,@whileAllocate};
Nfun = numel(funs) ;
%
Nalloc = 2E7 ;
Nsamp = 50 ;
%
x = linspace(0,1,Nalloc) ;
nIndex = round(logspace(1,log10(Nalloc),Nsamp)) ;
times = repmat({zeros(1,Nsamp)},1,Nfun) ;
% Array growth time-data
for k = 1:numel(funs)
f = funs{k};
for m = 1:Nsamp
tic;
f(x(1:nIndex(m)));
times{k}(m) = toc;
fprintf(['Iteration ',num2str(m,'%02G'),' of function ',num2str(k),' done.\n']);
end
end
% Plot
figure(1);
args(2:2:2*Nfun) = times;
args(1:2:2*Nfun) = repmat({nIndex},1,Nfun);
loglog(args{:});
legend('Dynamic Allocation (for)','Pre-Allocation (for)','Dyanmic Allocation (while)','Pre-Allocation (while)','Location','Northwest');
grid('on');
axis([nIndex(1),nIndex(end),0.95*min([times{:}]),1.05*max([times{:}])]);
xlabel('Number of Array Elements [-]');
ylabel('Elasped Time [s]');
% Switch to linear scale near allocation max and only look at for-functions
Nsamp = 50 ;
nIndex = round(10.^linspace(log10(Nalloc)-1.5,log10(Nalloc),Nsamp)) ;
mstart = repmat({zeros(1,Nsamp)},1,Nfun/2) ;
mend = repmat({zeros(1,Nsamp)},1,Nfun/2) ;
% Array growth memory-data
for k = 1:numel(funs)/2
f = funs{k};
for m = 1:Nsamp
[~,mstart{k}(m),mend{k}(m)] = f(x(1:nIndex(m)));
fprintf(['Iteration ',num2str(m,'%02G'),' of function ',num2str(k),' done.\n']);
end
end
% Plot
figure(2);
mem = [mstart,mend];
args(2:2:2*Nfun) = mem ;
args(1:2:2*Nfun) = repmat({nIndex},1,Nfun);
h = plot(args{:});
set(h([1,2]),'LineStyle','--');
set(h([1,3]),'Color','k');
set(h([2,4]),'Color','r');
legend('Dynamic Allocation Start','Pre-Allocation Start','Dynamic Allocation End','Pre-Allocation End','Location','Northwest');
grid('on');
axis([nIndex(1),nIndex(end),0.95*min([mem{:}]),1.05*max([mem{:}])]);
xlabel('Number of Array Elements [-]');
ylabel('Matlab Memory Usage [MB]');
end
function y = burden(x)
y = besselj(0,x);
end
function mem = getMemory()
mem = memory();
mem = mem.MemUsedMATLAB/1E6; %[MB]
end
function [y,mstart,mend] = forDynamic(x)
mstart = getMemory();
n = numel(x) ;
for k = 1:n
y(k) = burden(x(k));
end
mend = getMemory();
end
function [y,mstart,mend] = forAllocate(x)
mstart = getMemory();
n = numel(x) ;
y(1,n) = 0 ;
for k = 1:numel(x)
y(k) = burden(x(k));
end
mend = getMemory();
end
function [y,mstart,mend] = whileDynamic(x)
mstart = getMemory();
n = numel(x) ;
k = 1 ;
while k <= n
y(k) = burden(x(k)) ;
k = k + 1 ;
end
mend = getMemory();
end
function [y,mstart,mend] = whileAllocate(x)
mstart = getMemory();
n = numel(x) ;
k = 1 ;
y(1,n) = 0 ;
while k <= n
y(k) = burden(x(k)) ;
k = k + 1 ;
end
mend = getMemory();
end