高效使用内存的策略
本主题介绍在 MATLAB® 中高效使用内存的几种方法。
使用适当的数据存储
MATLAB 提供了不同大小的数据类(例如 double
和 uint8
),因此您无需使用大型类存储较小的数据段。例如,与使用 double
相比,使用 uint8
类存储 1,000 个无符号小整数值所用的内存少 7 KB。
使用相应的数值类
您应在 MATLAB 中使用的数值类取决于您的预期操作。默认类 double
可提供最佳精度,但存储每个元素需要 8 字节内存。如果您计划执行复杂的数学运算(例如线性代数),则您必须使用浮点类,例如 double
或 single
。single
类只需要 4 个字节。可使用 single
类执行的操作存在某些限制,但多数 MATLAB 数学运算都受支持。
如果您只需执行简单的算术运算并将原始数据表示为整数,则您可以在 MATLAB 中使用整数类。下面是数值类、内存要求(以字节为单位)及支持的运算的列表。
类(数据类型) | 字节 | 支持的运算 |
---|---|---|
single | 4 | 绝大多数的数学运算 |
double | 8 | 所有数学运算 |
logical | 1 | 逻辑/条件运算 |
int8, uint8 | 1 | 算术和某些简单函数 |
int16, uint16 | 2 | 算术和某些简单函数 |
int32, uint32 | 4 | 算术和某些简单函数 |
int64, uint64 | 8 | 算术和某些简单函数 |
减少存储数据时的开销量
MATLAB 数组(在内部作为 mxArrays
实现)需要一定的空间来将有关数据的元数据信息(例如类型、维度和属性)存储在内存中。每个数组大约需要 104 字节。仅当有大量(如数百或数千)较小的 mxArrays
(如标量)时,此开销才成问题。whos
命令列出了变量所用的内存,但不包括此开销。
因为简单数值数组(包括一个 mxArray
)的开销最少,所以您应该尽可能使用它们。当数据太复杂而无法存储在简单数组(或矩阵)中时,您可以使用其他数据结构体。
元胞数组由每个元素的单独 mxArrays
组成。因此,包含许多小元素的元胞数组具有较大的开销。
结构体的每个字段需要类似的开销量。包含许多字段和较少内容的结构体具有较大的开销,因此应避免使用这样的结构体。由包含数值标量字段的结构体组成的大型数组所需的内存要比具有包含较大数值数组的字段的结构体更多。
另请注意,虽然 MATLAB 将数值数组存储在连续内存中,但结构体和元胞数组则不然。有关详细信息,请参阅MATLAB 如何分配内存。
将数据导入相应的 MATLAB 类
当使用 fread
读取二进制文件中的数据时,常见的错误是,仅指定该文件中数据的类,而不指定 MATLAB 当文件位于工作区中时使用的数据的类。因此,即使您只读取 8 位值,使用的也是默认的 double
。例如,
fid = fopen('large_file_of_uint8s.bin', 'r'); a = fread(fid, 1e3, 'uint8'); % Requires 8k whos a Name Size Bytes Class Attributes a 1000x1 8000 double a = fread(fid, 1e3, 'uint8=>uint8'); % Requires 1k whos a Name Size Bytes Class Attributes a 1000x1 1000 uint8
尽可能使数组稀疏
如果您的数据包含许多零,请考虑使用稀疏数组,这样仅存储非零元素。以下示例比较稀疏存储和满存储的要求:
A = eye(1000); % Full matrix with ones on the diagonal As = sparse(A); % Sparse matrix with only nonzero elements whos Name Size Bytes Class Attributes A 1000x1000 8000000 double As 1000x1000 24008 double sparse
您可以看到,该数组只需要大约 24 KB 即可存储为稀疏数组,但要存储为满矩阵,则需要大约 8 MB。通常,对于包含 nnz
非零元素和 ncol
列的双精度型稀疏数组,所需的内存为:
16 *
nnz
+ 8 *ncol
+ 8 字节(在 64 位计算机上)
请注意,MATLAB 支持对稀疏数组执行大多数(但不是全部)数学运算。
避免临时性的数据副本
避免创建不必要的临时性数据副本,以显著减少所需的内存量。
避免创建临时数组
避免创建大型临时变量,并在不再需要这些临时变量时清除它们。例如,以下代码创建由零组成的、存储为临时变量 A
的数组,然后将 A
转换为单精度:
A = zeros(1e6,1); As = single(A);
使用一个命令来执行两个操作可更高效地使用内存:
A = zeros(1e6,1,'single');
使用嵌套函数减少传递的参量
处理大型数据集时,注意 MATLAB 会创建输入变量的临时副本(如果被调函数修改其值)。这会暂时使存储数组所需的内存翻倍,从而导致 MATLAB 在没有足够内存时生成错误。
在此情形下使用较少的内存的一种方法是使用嵌套函数。嵌套函数共享所有外部函数的工作区,为嵌套函数提供对其通常范围之外的数据的访问权。在如下示例中,嵌套函数 setrowval
可直接访问外部函数 myfun
的工作区,从而无需在函数调用中传递变量副本。当 setrowval
修改 A
的值时,它在调用函数的工作区中修改它。无需使用额外内存为所调用函数存储一个单独数组,且无需返回 A
的修改后的值:
function myfun A = magic(500); setrowval(400,0) disp('The new value of A(399:401,1:10) is') A(399:401,1:10) function setrowval(row,value) A(row,:) = value; end end
回收使用的内存
增加可用内存量的一种简单方法是清除您不再使用的大型数组。
定期将您的大型数据保存到磁盘
如果您的程序生成非常大量的数据,请考虑定期将数据写入磁盘。在保存该部分数据后,使用 clear
函数从内存中删除变量并继续生成数据。
从内存中清除不再需要的旧变量
当您重复或以交互方式处理非常大的数据集时,请首先清除旧变量以为新变量腾出空间。否则,MATLAB 需要等大小的临时存储才能覆盖此变量。例如,
a = rand(1e5); b = rand(1e5); Out of memory. More information clear a a = rand(1e5); % New array