MATLAB 如何分配内存

目录

为数组分配内存

复制数组

函数参数

数据类型和内存

数值数组

结构体和元胞数组

复数数组

稀疏矩阵

使用大数据集


        本主题提供关于 MATLAB® 在处理变量时如何分配内存的信息。这些信息,就像关于 MATLAB 内部如何处理数据的任何信息一样,在以后的版本中可能会变更。

为数组分配内存

        当将数值或字符数组分配给变量时,MATLAB 会分配一个连续的内存块,并将数组数据存储在该内存块中。MATLAB 还将有关数组数据的信息(如它的类和维度)存储在一个单独的小内存块标头。对于多数数组,存储标头所需的内存可忽略不计。然而,将大数据集存储在数量较少的大数组中比存储在数量较大的小数组中可能更理想。这是因为较少的数组需要较少的数组标头。

        如果向现有数组中添加新元素,MATLAB 会按照使内存存储保持连续的方式扩展该数组。这通常需要查找新的足以容纳扩展后的数组的内存块。MATLAB 随后将该数组的内容从其原始位置复制到内存中这一新块中,向该块中的数组添加新元素,并释放原始数组在内存中的位置。

        如果从现有数组中删除元素,MATLAB 会通过清除已删除的元素,然后使其在原始内存位置变得紧凑来使内存存储连续。

复制数组

​        当将数组分配给第二个变量时(例如,当您执行 B = A 时),MATLAB 不会立即分配新内存。此时,它会创建数组引用副本。只要不修改 A 和 B 引用的内存块的内容,就不需要存储多个数据副本。但是,如果使用 A 或 B 修改内存块的任何元素,MATLAB 就会分配新内存,将数据复制到其中,然后修改所创建的副本。

        在 Windows® 系统上,memory 函数可用于检查内存详细信息。要了解复制数组如何影响 Windows 系统的内存使用量,请在当前文件夹的一个文件中创建函数 memUsed。该函数调用 memory,以 MB 为单位返回MATLAB 进程使用的内存量。

function y = memUsed
usr = memory;
y = usr.MemUsedMATLAB/1e6;

        调用 memUsed 以显示当前内存使用量。

format shortG
memUsed
ans = 
       3966.1

        创建一个 2000×2000 数值数组,并观察内存使用量的变化。该数组使用大约 32 MB 的内存。

A = magic(2000);
memUsed
ans = 
       3998.1

        在 B 中制作 A 的副本。由于不需要使用数组数据的两个副本,MATLAB 仅创建数组引用的一个副本。这不需要额外增加大量内存。

B = A;
memUsed
ans = 
       3998.1

        现在通过删除 B 的行数的一半来修改它。由于 A 和 B 不再指向同一数据,MATLAB 必须为 B 分配一个单独的内存块。结果,MATLAB 进程使用的内存量增加了 B 的大小,约为 16 MB(即 A 所需的 32 MB 的一半)。

B(1001:2000,:) = [];

memUsed
ans = 
       4014.1

函数参数

        MATLAB 处理函数调用中传递的参数的方式与处理复制的数组的方式相同。将变量传递给函数时,实际是传递对该变量表示的数据的引用。只要被调用的函数未修改数据,调用函数或脚本中的变量和被调用函数中的变量就指向内存中的同一位置。如果被调用函数修改输入数据的值,则 MATLAB 将在内存中的新位置创建原始变量的副本,用修改后的值更新该副本,并将被调用函数中的输入参数指向此新位置。

        例如,假设有函数 myfun,它修改传递给它的数组的值。MATLAB 在内存中的新位置生成 A 的副本,将变量 X 设置为对此副本的引用,然后将 X 的一行设置为零。A 引用的数组保持不变。

A = magic(5);
myfun(A)

function myfun(X)
X(4,:) = 0;
disp(X)
end

        如果调用函数或脚本需要其传递给 myfun 的数组的修改后的值,您需要以被调用函数的输出形式返回更新的数组。

数据类型和内存

        MATLAB 的各数据类型的内存要求不同。通过了解 MATLAB 如何处理各种数据类型,有助于减少代码使用的内存量。

数值数组

​        MATLAB 分别对 8 位、16 位、32 位和 64 位有符号和无符号整数分配 1、2、4 或 8 个字节。它以双精度 (double) 或单精度 (single) 格式表示浮点数。由于 MATLAB 使用 4 个字节存储 single 类型的数值,因此与使用 8 位的 double 类型的数值相比,前者需要的内存更少。但是,由于它们是使用较少的位存储的,因此 single 类型的数值所呈现的精度要低于 double 类型的数值。在 MATLAB 中,double 是默认的数值数据类型,它可为大多数计算任务提供足够的精度。有关详细信息,请参阅浮点数。​

结构体和元胞数组

        数值数组必须存储在连续内存块中,但结构体和元胞数组可以存储在不连续内存块中。对于结构体和元胞数组,MATLAB 不仅为数组创建一个标头,还为结构体的每个字段及元胞数组的每个元胞创建一个标头。因此,存储结构体或元胞数组所需的内存量不仅取决于其包含的数据量,还取决于其构造方式。

        例如,假设有标量结构体 S1,它包含字段 R、G 和 B,其中每个字段包含一个 100×50 数组。S1 需要一个标头来描述总体结构,一个标头用于每个唯一字段名称,一个标头用于每个字段。这使得整个结构体总共有七个标头。

S1.R = zeros(100,50);
S1.G = zeros(100,50);
S1.B = zeros(100,50);

        另一方面,假设有一个 100×50 结构体数组 S2,其中每个元素都有标量字段 R、G 和 B。在本例中,S2 需要一个标头来描述总体结构,一个标头用于每个唯一字段名称,以及一个标头用于 5,000 个元素的每个字段,从而使整个结构体数组总共有 15,004 个数组标头。

for i = 1:100
    for j=1:50
        S2(i,j).R = 0;
        S2(i,j).G = 0;
        S2(i,j).B = 0;
    end
end

        使用 whos 函数比较在 64 位系统上分配给 S1 和 S2 的内存量。尽管 S1 和 S2 包含相同的数据,但 S1 使用的内存明显更少。

whos S1 S2
  Name        Size              Bytes  Class     Attributes

  S1          1x1              120504  struct              
  S2        100x50            1680192  struct          
复数数组

        MATLAB 使用复数的交错存储表示,其中实部和虚部一起存储在一个连续内存块中。如果创建复数数组的副本,然后仅修改该数组的实部或虚部,MATLAB 会创建一个同时包含实部和虚部的数组。

稀疏矩阵

​        使用稀疏存储来存储非零元素很少的矩阵是一种很好的做法。当一个满矩阵有少量非零元素时,将矩阵转换为稀疏存储通常会改善内存使用量和代码执行时间。可以使用 sparse 函数将满矩阵转换为稀疏存储。

        例如,假设矩阵 A 是 1000×1000 满存储单位矩阵。将 B 创建为 A 的稀疏副本。在稀疏存储中,相同的数据使用的内存量要少得多。

A = eye(1000);
B = sparse(A);
whos A B
  Name         Size                Bytes  Class     Attributes

  A         1000x1000            8000000  double              
  B         1000x1000              24008  double    sparse  

使用大数据集

        当处理大型数据集时,反复调整数组大小可能导致程序耗尽内存。如果您扩展数组使其超过其原始位置的可用连续内存,MATLAB 必须创建该数组的副本,并将副本移至具有足够空间的内存块中。在此过程中,内存中有原始数组的两个副本。这会暂时使数组所需的内存量翻倍,并增加您的程序出现内存不足的风险。

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MATLAB 中,预分配内存是一种优化技术,可以提高代码的执行效率。当你知道一个数组的最终大小时,可以使用预分配内存来避免动态分配内存的开销。 要预分配内存,你可以使用以下方法: 1. 使用空数组初始化变量:在创建变量时,使用类似于 `A = []` 的语句来初始化数组。然后,通过 分配给数组足够的元素,来为其分配所需的内存空间。例如,如果你知道一个数组最终将包含100个元素,可以使用 `A = zeros(1, 100)` 来预分配内存。 2. 使用 `zeros` 或 `ones` 函数创建数组:如果你知道数组的大小,并且所有元素的初始值都相同,可以使用 `zeros` 或 `ones` 函数来创建一个已经分配了内存的数组。例如,`A = zeros(1, 100)` 将创建一个包含100个元素的数组,初始值为0。 3. 使用 `NaN` 或其他特殊值初始化数组:如果你知道数组的大小,并且所有元素的初始值都是特殊值(如 `NaN`),可以使用类似于 `A = NaN(1, 100)` 的语句来创建一个已经分配了内存的数组。 请注意,在预分配内存后,确保不要超出数组的预分配大小。否则,MATLAB 将重新分配更大的内存空间,这会导致性能下降。 预分配内存是一种有效的优化技术,特别适用于在循环中使用的大型数组。通过避免动态分配内存,可以减少内存分配和释放的开销,从而提高代码的执行速度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值