预分配的重要性
MATLAB 中的数组作为连续块保存在内存中,由 MATLAB 自动分配和释放。MATLAB 隐藏了内存管理操作,例如在易于使用的语法背后调整数组大小:
a = 1:4
a =
1 2 3 4
a(5) = 10 % or alternatively a = [a, 10]
a =
1 2 3 4 10
重要的是要理解上面不是一个简单的操作,a(5) = 10
将导致 MATLAB 分配一个大小为 5 的新内存块,复制前 4 个数字,并将第 5 个数字设置为 10.这是一个 O(numel(a))
操作,而不是 O(1)
。
考虑以下:
clear all
n=12345678;
a=0;
tic
for i = 2:n
a(i) = sqrt(a(i-1)) + i;
end
toc
Elapsed time is 3.004213 seconds.
a
在此循环中重新分配了 5 次(不包括 MATLAB 进行的一些优化)! 请注意,MATLAB 给出了一个警告:
“变量’a’似乎在每次循环迭代时改变大小。考虑预先分配速度。”
我们预先分配时会发生什么?
a=zeros(1,n);
tic
for i = 2:n
a(i) = sqrt(a(i-1)) + i;
end
toc
Elapsed time is 0.410531 seconds.
我们可以看到运行时间减少了一个数量级。
预分配方法:
MATLAB 根据用户的具体要求提供各种矢量和矩阵分配功能。这些包括: zeros
, ones
, nan
, eye
, true
等。
a = zeros(3) % Allocates a 3-by-3 matrix initialized to 0
a =
0 0 0
0 0 0
0 0 0
a = zeros(3, 2) % Allocates a 3-by-2 matrix initialized to 0
a =
0 0
0 0
0 0
a = ones(2, 3, 2) % Allocates a 3 dimensional array (2-by-3-by-2) initialized to 1
a(:,:,1) =
1 1 1
1 1 1
a(:,:,2) =
1 1 1
1 1 1
a = ones(1, 3) * 7 % Allocates a row vector of length 3 initialized to 7
a =
7 7 7
还可以指定数据类型:
a = zeros(2, 1, 'uint8'); % allocates an array of type uint8
克隆现有数组的大小也很容易:
a = ones(3, 4); % a is a 3-by-4 matrix of 1's
b = zeros(size(a)); % b is a 3-by-4 matrix of 0's
并克隆类型:
a = ones(3, 4, 'single'); % a is a 3-by-4 matrix of type single
b = zeros(2, 'like', a); % b is a 2-by-2 matrix of type single
注意’喜欢’也克隆了复杂性和稀疏性。
使用任何返回最终所需大小数组的函数隐式实现预分配,例如 rand
, gallery
, kron
, bsxfun
, colon
等等。例如,分配具有线性变化元素的向量的常用方法是使用冒号运算符(使用 2 或 3 操作数变量 1 ):
a = 1:3
a =
1 2 3
a = 2:-3:-4
a =
2 -1 -4
可以使用 cell()
函数分配单元阵列,其方式与 zeros()
非常相似。
a = cell(2,3)
a =
[] [] []
[] [] []
请注意,单元格数组的工作方式是将指针指向单元格内容的内存中的位置。因此,所有预分配技巧也适用于单个单元阵列元素。
进一步阅读:
- 关于预分配内存的官方 MATLAB 文档 。
- 关于MATLAB 如何分配内存的官方 MATLAB 文档 。
- 未记录的 matlab 上的预分配性能 。
- 了解阵列预分配上 基于 MATLAB 的艺术洛伦