MATLAB 中高效内存分配的重要性

2025年7月25日 | 阅读 8 分钟

引言

内存分配是一个重要的过程,它定义了程序在运行时如何存储、访问和操作数据。MATLAB 是一种用于数值计算和图形可视化的高级语言;它使用复杂的内存管理来提供最佳输出和用户友好性。

  • 静态内存分配: 这是因为内存大小是在编译时确定的属性。这种方法不是很灵活,通常在 C 等低级语言中实现。
  • 动态内存分配: 在程序执行期间动态启动内存访问,以提供更高的多功能性,同时降低内存控制的复杂性。

MATLAB 主要采用动态内存分配,并且在数据的大小、类型和操作方面具有灵活性。

MATLAB 中高效的内存分配

  • 处理大型数据集: MATLAB 广泛用于计算,其中数据可能很大且需要数值分析。如果内存分区做得不好,可能会耗尽内存,或者程序运行缓慢。
  • 性能优化: 这是因为糟糕的内存管理会导致大量资源浪费,因此系统需要大量资源来执行大量计算,从而导致整体工作缓慢。
  • 增强用户体验: MATLAB 的最大优势之一是自动管理的内存,无论用户是否具有编程经验,用户的直接交互都很容易。
  • 可伸缩性: 内存优化对于扩展算法和应用程序的规模以执行更困难的计算或更高的数据量至关重要。

MATLAB 内存管理的关键特性

  • 动态数组: MATLAB 提供了根据数据可用性增长或缩小数组维度的功能。例如,当向矩阵添加元素时,会为该空间创建内存。

示例

  • 预分配建议: 在 MATLAB 中,动态大小是可以理解的,但为数组进行初始内存分配更有效。这有助于避免持续调用内存用于其他用途所带来的开销。
  • 写时复制机制: MATLAB 不会像其他计算机语言那样以相同的方式复制独立变量。内存共享直到发生修改,这是为了提高性能和优化内存。
  • 垃圾回收: 每当用户完成一个变量时,MATLAB 会根据用户的功能删除它,从而减少内存泄漏的情况。
  • 内存分析工具: memory、profile 和 who 是内置工具,可帮助分析和优化脚本和函数中的内存利用率。

MATLAB 中的内存类型

1. 堆内存

堆用于动态内存分配;因此,MATLAB 可以在程序运行时使用具有不同大小的可变内存。

  • 当变量的大小在编译时未知时,例如单元格数组、数组和结构体,它会使用堆。
  • 当变量增加或改变其内容时,MATLAB 会自动创建和扩展内存空间。

存储在堆内存中的变量示例

矩阵和数组

单元格数组

结构

优点

  • 当变量大小可能发生变化时,一个不错的选择。
  • 使对象和复杂结构等数据形式可见。

2. 栈内存

栈内存用于临时自动变量、函数调用和返回等。与数组一样,它遵循后进先出 (LIFO) 技术,并且比堆内存小。

  • 特定函数的参数和局部变量。
  • 常用变量
  • 从函数中检索值。

利用栈内存的操作示例

函数中的局部变量

优点

  • 由于堆栈结构井井有条,因此可以轻松访问临时变量。
  • 自我管理,因此没有内存泄漏问题。

3. 全局内存

全局内存用于存储变量,这些变量旨在在不同的函数和不同的工作区中多次使用,但不是参数。

  • 在作用域之外声明的变量称为“全局变量”,它们使用“global”关键字声明。
  • 这些变量存在于一个全局内存域中,任何函数如果声明了这些变量,都可以访问它们。

声明和访问全局变量

优点

  • 适用于以不方便传递参数的方式在功能模块之间共享数据。
  • 适用于应用程序常量或在整个应用程序中使用相同值的情况。

MATLAB 中的内存分配策略

1. 动态内存分配

动态内存分配 MATLAB 中的动态内存分配定义了一种情况,即程序中分配给变量的内存量在程序运行时发生变化。与必须在运行时初始化变量大小的静态分配语言不同,MATLAB 根据变量的使用情况灵活地增加变量的大小。

  • 每当添加许多新元素时,MATLAB 都可以自动调整矩阵和数组的大小。

示例

优点

  • 使编程变得更容易,因为用户不必一直记住数组大小。
  • 有助于实践常识性编码方法,即添加元素而无需担心内存分配。

2. 预分配

预分配是在对数组或矩阵执行计算之前声明所需内存量的过程。此技术通过预先确定内存块的创建来消除动态调整大小中存在的开销问题。

  • 性能提升: 预分配空间有助于避免分配片段然后逐步增加片段大小的需要,从而产生片段分配的开销。
  • 减少开销: MATLAB 不会尽可能频繁地调用内存管理例程,因此将提供更快的运行时间。

语法和示例

MATLAB 语言提供了在编写代码之前使用其中编程的确定数据来构建数组的功能。最常用的函数包括

1. zeros():创建一个填充零的数组。

2. ones():创建一个填充一的数组。

3. NaN():创建一个填充 NaN 值的数组(用于占位符)。

4. 自定义初始化:您可以组合函数进行初始化

动态调整大小

预分配

3. 延迟复制(写时复制机制)

MATLAB 使用延迟复制策略,这是一种写时复制机制,用于控制大型数组的内存使用。当复制一个变量时,会创建另一个变量来包含其数据。MATLAB 直到第二个副本以某种方式更改后才制作数据的另一个副本。

仅当新变量 (B) 被修改时才复制内存

优点

  • 内存效率: 避免不必要的复制的时间和限制,而大型数据通常存储在内存中。
  • 性能: “绕过”内存复制,直到需要时才进行,这通常比其他情况花费更少的时间。

影响 MATLAB 内存分配的因素

1. 变量类型

数值变量

  • 数值数据类型包括 double(默认)、single 和 integer(int、int8、int16 等),它们定义了使用的内存量。
  • 例如,一个双精度变量表示每个元素 8 字节,一个单精度变量表示每个元素 4 字节。

字符串变量

  • MATLAB 中的字符串是字符数组,每个字符的大小在计算机内存中为 2 字节。
  • 字符串数组比通常的类型占用更多的内存空间,因为它们有额外的元数据空间。

单元格数组

  • 单元格数组是一种以可变大小和数据类型的单维数组存储数据的方式,但代价是内存方面的小开销。
  • 单元格数组中的每个元素都指向一个组件,并且保留一些其他内存空间来存储数据。

2. 变量大小

小数组与大数组

  • 小数组大多位于 MATLAB 的预分配内存中,因此比其他数据获得更多关注,从而更快地访问。
  • 在列表中,大型数组,尤其是在循环中动态增长的数组,可能会导致多次内存重新分配步骤,这代价高昂。

稀疏矩阵

  • 稀疏矩阵被认为是识别和管理大量数据(以大量零为特征)的有效方法。
  • 例如,当我们尝试将一个 1000 x 1000 的稀疏矩阵(仅包含 1% 的实际值)转换为完整矩阵时,它占用的内存更少。

多维数组

  • 随着数组维度的增加,用于索引元数据的内存空间量会增加,除了数组大小本身。

代码效率

矢量化操作

  • MATLAB 提高了计算速度,因为与某些编程语言不同,它旨在对数组和矩阵执行操作,而不是按顺序元素。
  • 例如,您可以使用以下代码而不是使用循环来添加两个数组的元素

基于循环的操作

  • 循环可能会导致内存浪费问题,尤其是在循环中尝试调整数组大小时。
  • 例如,这样的代码会导致额外的内存重新分配过程
  • 虽然这会在循环之前分配数组,但它将防止上面遇到的错误

临时变量

  • 创建不必要的临时变量会增加内存使用量。通过重写代码减少此类变量可以节省大量内存。