结果可能会产生误导。 当您将两个空矩阵相乘时,生成的矩阵不会立即“分配”和“初始化”,而是会推迟到您第一次使用它时(有点像惰性计算)。
当索引超出范围以增长变量时,情况也是如此,在数字数组的情况下,该变量用零填充所有缺失的条目(我将在后面讨论非数字的情况)。 当然,以这种方式生长矩阵不会覆盖现有元素。
因此,虽然看起来更快,但是您只是在延迟分配时间,直到您真正开始使用矩阵。 最后,您将拥有与开始时一样的分配时间。
与其他几种选择相比,展示此行为的示例:
N = 1000;
clear z
tic, z = zeros(N,N); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
clear z
tic, z = zeros(N,0)*zeros(0,N); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
clear z
tic, z(N,N) = 0; toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
clear z
tic, z = full(spalloc(N,N,0)); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
clear z
tic, z(1:N,1:N) = 0; toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
clear z
val = 0;
tic, z = val(ones(N)); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
clear z
tic, z = repmat(0, [N N]); toc
tic, z = z + 1; toc
assert(isequal(z,ones(N)))
结果显示,如果对两种情况下的两条指令的经过时间进行求和,最终将得到相似的总计时:
// zeros(N,N)
Elapsed time is 0.004525 seconds.
Elapsed time is 0.000792 seconds.
// zeros(N,0)*zeros(0,N)
Elapsed time is 0.000052 seconds.
Elapsed time is 0.004365 seconds.
// z(N,N) = 0
Elapsed time is 0.000053 seconds.
Elapsed time is 0.004119 seconds.
其他时间是:
// full(spalloc(N,N,0))
Elapsed time is 0.001463 seconds.
Elapsed time is 0.003751 seconds.
// z(1:N,1:N) = 0
Elapsed time is 0.006820 seconds.
Elapsed time is 0.000647 seconds.
// val(ones(N))
Elapsed time is 0.034880 seconds.
Elapsed time is 0.000911 seconds.
// repmat(0, [N N])
Elapsed time is 0.001320 seconds.
Elapsed time is 0.003749 seconds.
这些测量值在毫秒内太小,可能不太准确,因此您可能想循环运行这些命令数千次并取平均值。 另外有时运行保存的M函数比运行脚本或在命令提示符下运行更快,因为某些优化只会以这种方式发生...
无论哪种方式,分配通常都会执行一次,因此谁在乎是否需要额外的30毫秒:)
单元格阵列或结构阵列可以看到类似的行为。 考虑以下示例:
N = 1000;
tic, a = cell(N,N); toc
tic, b = repmat({[]}, [N,N]); toc
tic, c{N,N} = []; toc
这使:
Elapsed time is 0.001245 seconds.
Elapsed time is 0.040698 seconds.
Elapsed time is 0.004846 seconds.
请注意,即使它们都相等,它们占用的内存量也不同:
>> assert(isequal(a,b,c))
>> whos a b c
Name Size Bytes Class Attributes
a 1000x1000 8000000 cell
b 1000x1000 112000000 cell
c 1000x1000 8000104 cell
实际上,这里的情况更加复杂,因为MATLAB可能为所有单元共享相同的空矩阵,而不是创建多个副本。
单元格数组mxCreateUninitDoubleMatrix实际上是未初始化的单元格数组(NULL指针的数组),而mxCreateUninitNumericArray是单元格数组,其中每个单元格都是空数组mxCreateUninitNumericMatrix(内部并且由于数据共享,只有第一个单元格b{1}指向[] 而其余所有都引用第一个单元格)。 最终数组c与a(未初始化的单元格)相似,但最后一个数组包含空数值矩阵[]。
我浏览了mxCreateUninitDoubleMatrix(使用Dependency Walker工具)导出的C函数的列表,发现了一些有趣的东西。
有一些未记录的函数可用于创建未初始化的数组:mxCreateUninitDoubleMatrix,mxCreateUninitNumericArray和mxCreateUninitNumericMatrix。实际上,文件交换上有一个提交使用这些函数为mxCalloc函数提供了更快的替代方法。
有一个未记录的函数,名为mxCalloc。在线搜索,我可以看到您也将这个问题交叉发布在MATLAB Answers上,那里有一些出色的答案。 James Tursa(之前是UNINIT的同一作者)举了一个有关如何使用此未记录功能的示例。
mxCalloc与mxMalloc共享库链接。 这是Intel TBB可扩展内存分配器。 该库提供了针对并行应用程序优化的等效内存分配功能(mxRealloc、calloc、free)。 请记住,许多MATLAB函数是自动多线程的,因此如果矩阵大小足够大,如果zeros(..)是多线程的并且正在使用Intel的内存分配器,我不会感到惊讶(这是Loren Shure的最新评论证实了这一事实)。
关于内存分配器的最后一点,您可以在C / C ++中编写类似于@PavanYalamanchili的基准,并比较可用的各种分配器。 这样的事情。 请记住,MEX文件的内存管理开销略高,因为MATLAB使用mxCalloc、mxMalloc或mxRealloc函数会自动释放MEX文件中分配的所有内存。 值得一试的是,以前可以在旧版本中更改内部内存管理器。
编辑:
这是一个比较详尽的基准,用于比较讨论的替代方案。 它具体表明,一旦强调使用整个分配的矩阵,所有这三种方法都处于平等地位,并且差异可以忽略不计。
function compare_zeros_init()
iter = 100;
for N = 512.*(1:8)
% ZEROS(N,N)
t = zeros(iter,3);
for i=1:iter
clear z
tic, z = zeros(N,N); t(i,1) = toc;
tic, z(:) = 9; t(i,2) = toc;
tic, z = z + 1; t(i,3) = toc;
end
fprintf('N = %4d, ZEROS = %.9f\n', N, mean(sum(t,2)))
% z(N,N)=0
t = zeros(iter,3);
for i=1:iter
clear z
tic, z(N,N) = 0; t(i,1) = toc;
tic, z(:) = 9; t(i,2) = toc;
tic, z = z + 1; t(i,3) = toc;
end
fprintf('N = %4d, GROW = %.9f\n', N, mean(sum(t,2)))
% ZEROS(N,0)*ZEROS(0,N)
t = zeros(iter,3);
for i=1:iter
clear z
tic, z = zeros(N,0)*zeros(0,N); t(i,1) = toc;
tic, z(:) = 9; t(i,2) = toc;
tic, z = z + 1; t(i,3) = toc;
end
fprintf('N = %4d, MULT = %.9f\n\n', N, mean(sum(t,2)))
end
end
下面是根据矩阵大小增加而在100次迭代中平均的时序。 我在R2013a中进行了测试。
>> compare_zeros_init
N = 512, ZEROS = 0.001560168
N = 512, GROW = 0.001479991
N = 512, MULT = 0.001457031
N = 1024, ZEROS = 0.005744873
N = 1024, GROW = 0.005352638
N = 1024, MULT = 0.005359236
N = 1536, ZEROS = 0.011950846
N = 1536, GROW = 0.009051589
N = 1536, MULT = 0.008418878
N = 2048, ZEROS = 0.012154002
N = 2048, GROW = 0.010996315
N = 2048, MULT = 0.011002169
N = 2560, ZEROS = 0.017940950
N = 2560, GROW = 0.017641046
N = 2560, MULT = 0.017640323
N = 3072, ZEROS = 0.025657999
N = 3072, GROW = 0.025836506
N = 3072, MULT = 0.051533432
N = 3584, ZEROS = 0.074739924
N = 3584, GROW = 0.070486857
N = 3584, MULT = 0.072822335
N = 4096, ZEROS = 0.098791732
N = 4096, GROW = 0.095849788
N = 4096, MULT = 0.102148452