matlab预设一个空矩阵,通过空矩阵乘法初始化数组的更快方法?(Matlab)

本文探讨了在Matlab中使用不同方法预设空矩阵并初始化数组的效率,包括直接使用`zeros`、空矩阵乘法、动态增长等。实验结果显示,虽然空矩阵乘法在初期看似更快,但实际分配时间会被推迟到首次使用时,与其他方法的总时间相近。文章还涉及了内存分配、单元格数组和结构数组的行为,以及未记录的函数`mxFastZeros`。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结果可能会产生误导。当您将两个空矩阵相乘时,生成的矩阵不会立即“分配”和“初始化”,而是会推迟到您第一次使用它时(有点像惰性计算)。

当索引超出范围以增长变量时,情况也是如此,在数字数组的情况下,该变量将用零填充所有缺失的条目(我将在后面讨论非数字的情况)。当然,以这种方式生长矩阵不会覆盖现有元素。

因此,虽然看起来更快,但是您只是在延迟分配时间,直到您真正开始使用矩阵。最后,您将拥有与开始时一样的分配时间。

与其他几种选择相比,展示此行为的示例:

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可能为所有单元共享相同的空矩阵,而不是创建多个副本。

a实际上,单元格数组是未初始化单元格的数组(NULL指针的数组),而b单元格数组是每个单元格都是空数组的单元格数组[](内部并且由于数据共享,只有第一个单元格b{1}指向[]而其余所有单元格都指向对第一个单元格的引用)。最终数组c类似于a(未初始化的单元格),但最后一个数组包含一个空数值矩阵[]。

我浏览了libmx.dll使用Dependency Walker工具从中导出的C函数的列表,发现了一些有趣的东西。

有用于创建初始化数组无证功能:mxCreateUninitDoubleMatrix,mxCreateUninitNumericArray,和mxCreateUninitNumericMatrix。实际上,文件交换上有一个提交使用这些功能来提供功能的更快替代方案zeros。

存在一个未记录的函数,称为mxFastZeros。在线上进行谷歌搜索,我还可以看到您也将此问题交叉发布在MATLAB Answers上,那里有一些出色的答案。James Tursa(之前是UNINIT的同一作者)举了一个有关如何使用此未记录功能的示例。

libmx.dll与tbbmalloc.dll共享库链接。这是Intel TBB可扩展内存分配器。该库提供等效内存分配函数(malloc,calloc,free),用于并行应用进行了优化。请记住,许多MATLAB函数是自动多线程的,因此zeros(..)一旦矩阵大小足够大,如果使用多线程并使用Intel的内存分配器,我不会感到惊讶(这是Loren Shure的最新评论证实了这一事实)。

关于内存分配器的最后一点,您可以在C / C ++中编写类似于@PavanYalamanchili的基准,并比较可用的各种分配器。像这样的东西。请记住,MEX-文件有一个略高的内存管理开销,因为MATLAB自动释放所有正在使用中MEX-文件分配的任何内存mxCalloc,mxMalloc或mxRealloc功能。值得一试的是,以前可以在旧版本中更改内部内存管理器。

编辑:

这里是比较详尽的基准,用于比较讨论的替代方案。它具体表明,一旦强调使用整个分配的矩阵,所有这三种方法都处于平等地位,并且差异可以忽略不计。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值