MATLAB

工作区保存成 .mat 文件:Save Workspace As 把变量存盘,load 一键恢复

👤 为我痴狂 👁 3 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-10-12
首页› 理学› MATLAB› 正文
工作区保存成 .mat 文件:Save Workspace As 把变量存盘,load 一键恢复

从 save/load 的底层字节布局,到 -v7.3 HDF5、跨语言互操作与云端持久化的工程实践

技术深度 · 工程路径 · 前沿预判

摘要

工作区持久化看似只是点一下“Save Workspace As”,但它牵涉到数据序列化格式、内存布局、跨版本兼容、跨语言互操作以及大规模数据工程等一系列问题。本文以 MATLAB 的 save / load 为主线,逐层拆解 .mat 文件的版本演进(v4、v6、v7、v7.3)、HDF5 容器结构、变量序列化规则、结构体与元胞数组的嵌套存储、稀疏矩阵与对象句柄的处理方式,并进一步延伸到分块存储、内存映射、云端对象存储、Python/Julia/R 互操作等工程场景。全文贯穿一条主线:持久化不是“存下来”这么简单,而是“以可被未来工具正确还原的方式存下来”。围绕这条主线,本文给出可复现的操作步骤、参数对照表、性能取舍建议,并对 MAT 格式在异构计算时代的位置做出独立判断。

1. 为什么“存盘”是一个被低估的工程问题

在 MATLAB 的日常使用中,save 和 load 几乎是所有教程里最先出现的两条命令。它们简单到让人觉得不需要解释:一条把变量写进 .mat 文件,一条把它读回来。但真正在工程环境里跑过几年项目的人,几乎都踩过这样的坑:换了一台机器,load 报版本不兼容;文件明明只有 2 GB,却提示“变量过大无法保存”;用 Python 读 .mat 时结构体字段全变成了奇怪的对象;把文件传到云端再拉回来,校验和变了。

这些问题的根源,是很多人把“持久化”理解成了“把内存里的东西倒到磁盘上”。而真正意义上的持久化,是让数据在未来某个时间点、被某个可能不同的工具、以正确的语义还原出来。这中间至少涉及四层问题:格式层(字节怎么排)、语义层(类型怎么表达)、工具层(谁来读)、治理层(版本怎么管)。save 和 load 只是这四层里最上面的一层薄壳。

本文评述:笔者认为,把 .mat 文件类比成“MATLAB 的私有数据库”比类比成“文本文件”更贴切。它有自己的 schema(变量名、类型、维度)、有自己的存储引擎(v4 的流式布局 vs v7.3 的 HDF5)、有自己的版本迁移问题。一旦用数据库的视角去看待它,很多“玄学问题”就变成了可分析、可规避的工程问题。

从数据量角度看,这种视角转变的必要性越来越强。根据 MathWorks 官方文档与社区统计(MathWorks Documentation, 2024),MATLAB 用户中处理超过 1 GB 工作区数据的比例在过去五年显著上升,而 v7 格式对单个变量的 2 GB 硬上限,正是大量“保存失败”报错的直接原因。这不是一个可以靠“多按几次 Ctrl+S”解决的问题,它需要理解格式本身。

1.1 一条贯穿全文的主线

本文的主线可以概括为一句话:持久化的本质是“契约”,而不是“拷贝”。你写下的每一个 .mat 文件,都是对未来的自己(或同事、或另一个程序)的一份契约:我承诺这些字节能被还原成这些变量。契约能不能兑现,取决于格式版本、类型表达、工具支持三者是否对齐。后续每一节,都会回到这条主线上来。

2. .mat 文件格式的版本谱系与底层结构

要理解 save 的行为,必须先知道它写出来的是什么。.mat 并不是一种单一格式,而是一族格式的统称。不同版本在字节布局、压缩方式、支持的数据类型上差异很大。

2.1 版本谱系

版本标识 底层容器 单变量上限 压缩 典型场景
-v4 自定义二进制流 约 100 MB 量级 无 极老版本兼容
-v6 自定义二进制流 约 2 GB 可选 zlib MATLAB 5–6 时代
-v7(默认) 自定义二进制流 2 GB 硬上限 默认 zlib 日常通用
-v7.3 HDF5(.h5 子集) 受 HDF5 与文件系统限制 HDF5 内建 大文件、跨语言

这张表里最值得注意的一行是 -v7.3。它不是一个“更好的 v7”,而是一次容器层面的切换:从 MathWorks 自研的二进制流,换成了 HDF5。这个切换带来的连锁反应,是本文后半部分很多讨论的起点。根据 HDF Group 的技术文档(The HDF Group, 2023),HDF5 采用分层结构:文件是根组,组下面可以挂数据集(dataset)和属性(attribute),数据集支持分块(chunking)、压缩、扩展维度。MATLAB 把每个变量映射成根组下的一个数据集,把变量名、类型等元信息放进属性里。

本文评述:笔者认为,-v7.3 的真正价值不在于“能存更大”,而在于“能被别人读”。HDF5 是一个有独立规范、有 C/Fortran/Python/Java 多语言绑定的开放格式。一旦数据落在 HDF5 里,它就脱离了 MATLAB 的私有生态,进入了科学计算的公共生态。这是从“私有格式”到“公共契约”的一次跃迁,代价是文件结构更复杂、小文件开销更大。

2.2 一个最小 .mat 文件的字节解剖

以 v7 格式为例,文件开头是 128 字节的头部。前 116 字节是描述性文本(通常包含创建平台、时间等信息),随后是 8 字节的子系统数据偏移量,再是 2 字节的版本号,最后 2 字节的“endian indicator”用来标识字节序(IM 表示小端,MI 表示大端)。这个设计让读取方在解析任何数据之前,就能判断自己能不能读、以什么字节序读。

% 查看 .mat 文件头部信息(示意)
fid = fopen('data.mat','r');
header = fread(fid, 128, '*uint8');
fclose(fid);
char(header(1:16))'   % 描述性文本片段
header(125:126)'      % 版本号
char(header(127:128)) % 字节序标识

128 字节之后是数据元素(data element)的连续排列。每个元素有 8 字节的 tag:4 字节数据类型 + 4 字节字节数,后面跟数据本体。这种“tag-length-value”式的布局,是很多二进制格式的经典做法。它的优点是解析简单、顺序读取快;缺点是随机访问需要从头扫描,且不支持原地扩展。

本文评述:笔者认为,v7 的流式布局和 v7.3 的 HDF5 布局,代表了两种截然不同的设计哲学。前者优化“顺序写、顺序读”的批处理场景,后者优化“按需读、部分读、跨工具读”的交互场景。选择哪个版本,本质上是在选择你的数据未来会被怎样使用。如果数据只是自己当天用完就删,v7 足够;如果数据要进数据湖、要被多个团队读、要保留数年,v7.3 是更负责任的选择。

3. save 的参数体系:从 -v7.3 到 -append 的完整拆解

MATLAB 的 save 命令参数很多,但真正影响工程决策的其实就那么几个。这一节把常用参数按“影响格式”“影响内容”“影响行为”三类拆开讲。

3.1 影响格式的参数

-v7.3 是最常用的一个。它的触发条件往往是“文件超过 2 GB”或“需要 Python 读”。但要注意,-v7.3 写出的文件在旧版 MATLAB(R2006b 之前)里无法读取,这在需要向下兼容的团队里是个硬约束。-v6 和 -v4 现在主要用于对接老旧系统或特定硬件工具链。

-nocompression 值得单独说。v7 默认开启 zlib 压缩,这对文本型、稀疏型数据压缩率很高,但对已经压缩过的数据(如 JPEG 图像、某些浮点噪声)几乎无效,反而增加 CPU 开销。在需要极致写入速度的场景(如实时采集落盘),关掉压缩可能让吞吐提升明显。具体提升幅度取决于数据可压缩性,建议用 tic/toc 实测。

3.2 影响内容的参数

参数 作用 工程建议
-regexp 用正则匹配变量名 批量保存前缀相同的变量
-struct 把结构体字段拆成独立变量 慎用,会破坏命名空间
-append 向已有文件追加变量 仅 v6/v7 支持,v7.3 不支持
-ascii 以文本格式保存 仅调试用,精度会损失

-append 是个容易踩坑的参数。它在 v7 下可以往已有文件里加变量,但如果新变量与已有变量同名,会直接覆盖,且不给出警告。更麻烦的是,-append 与 -v7.3 不能同时使用。在需要增量写入的场景,更稳妥的做法是用 HDF5 的底层接口(h5write)直接操作数据集,而不是依赖 save -append。

本文评述:笔者认为,-append 的设计反映了早期 MATLAB 对“文件即工作区快照”的定位,而不是“文件即数据库”。一旦你把 .mat 当成需要频繁更新的数据容器,就应该转向 HDF5 接口或干脆换用其他存储方案。用 save -append 做增量更新,长期看是技术债。

3.3 一个可复用的保存函数模板

function saveWorkspaceSafe(outPath, varargin)
% 安全保存工作区:自动选择格式、记录元信息、校验写入
    p = inputParser;
    addParameter(p, 'Include', {});      % 要保存的变量名
    addParameter(p, 'Exclude', {});      % 要排除的变量名
    addParameter(p, 'Force73', false);   % 强制 v7.3
    parse(p, varargin{:});
    opts = p.Results;

    vars = evalin('base', 'who');
    if ~isempty(opts.Include)
        vars = intersect(vars, opts.Include);
    end
    if ~isempty(opts.Exclude)
        vars = setdiff(vars, opts.Exclude);
    end

    % 估算总大小,决定格式
    info = whos('-regexp', strjoin(vars, '|'));
    totalBytes = sum([info.bytes]);
    use73 = opts.Force73 || totalBytes > 1.5e9;

    if use73
        save(outPath, vars{:}, '-v7.3');
    else
        save(outPath, vars{:}, '-v7');
    end

    % 写入后校验
    d = dir(outPath);
    fprintf('Saved %d vars, %.2f MB, format=%s\n', ...
        numel(vars), d.bytes/1e6, ternary(use73,'v7.3','v7'));
end

function r = ternary(c, a, b)
    if c, r = a; else, r = b; end
end

这个模板做了三件工程上很实用的事:一是根据数据量自动选格式,避免手动判断;二是支持白名单/黑名单,避免把临时变量也存进去;三是保存后回读文件大小并打印,便于日志追踪。它不是“最优”实现,但比裸调 save 更接近生产环境的要求。

4. load 的还原语义:作用域、覆盖与懒加载

load 看起来比 save 简单,但它有几个容易忽略的语义细节,直接影响代码的正确性。

4.1 输出形式决定作用域

load('data.mat') 不带输出参数时,会把变量直接注入当前工作区。如果当前工作区已有同名变量,会被静默覆盖。这在函数内部尤其危险:一个函数里调用 load,可能无意中覆盖了函数的局部变量,而且不报错。

S = load('data.mat') 带输出参数时,返回一个结构体,字段名就是变量名。这种方式不会污染工作区,是函数内部读取数据的推荐写法。代价是多一层结构体访问,以及对于超大变量,结构体包装本身有轻微内存开销。

% 推荐:函数内部用结构体形式,避免污染
function result = analyze(filePath)
    S = load(filePath);
    if ~isfield(S, 'signal')
        error('缺少 signal 变量');
    end
    result = process(S.signal);
end

% 不推荐:直接 load 进函数工作区
function result = analyzeBad(filePath)
    load(filePath);   % signal 变量凭空出现,可读性差
    result = process(signal);
end

4.2 选择性加载与懒加载

当 .mat 文件很大时,全量加载既慢又占内存。v7.3 格式支持按变量选择性加载:load('big.mat', 'varA', 'varB') 只读指定变量。在 HDF5 层面,MATLAB 会只读取对应的数据集,而不是扫描整个文件。这是 v7.3 相对 v7 的一个实质性优势。

更进一步的懒加载思路,是用 matfile 函数。它返回一个类似文件句柄的对象,支持按索引读取数组的一部分,而不需要把整个变量读进内存。

% matfile 懒加载示例
m = matfile('largeData.mat');        % 不立即读数据
sz = size(m.bigMatrix);              % 只读元信息
chunk = m.bigMatrix(1:1000, :);      % 只读前 1000 行
m.bigMatrix(5001:6000, :) = newData; % 支持部分写入(v7.3)

本文评述:笔者认为,matfile 是 MATLAB 里被严重低估的一个工具。它把 .mat 文件从“整存整取”的容器,变成了“可随机访问”的数据源。对于超出内存的数据集,这是比“分块存多个文件”更优雅的方案。但它只支持 v7.3 格式,且部分写入需要文件系统支持,在 NFS 等网络文件系统上行为可能不稳定,需要实测。

4.3 版本兼容的还原边界

一个常被问到的问题是:新版 MATLAB 存的 .mat,旧版能读吗?答案取决于格式和数据类型。v7 文件在 R2006b 之后的版本基本都能读;v7.3 需要 R2006b 之后且开启了 HDF5 支持的版本。但即使格式能读,某些新数据类型(如 string 类型、table、datetime)在旧版里可能被还原成结构体或直接报错。这是一个语义层的兼容问题,不是字节层的问题。

工程上的应对策略是:如果数据需要长期归档且可能被旧工具读取,保存时主动降级为“基础类型”——把 string 转成 char,把 table 转成结构体数组。这会在当下损失一些便利,但换来的是更长的可读寿命。

5. 复杂数据类型的序列化:结构体、元胞、稀疏与对象

基础数值数组的序列化很直接:维度信息加连续内存块。但 MATLAB 的数据类型远不止数值数组,结构体、元胞数组、稀疏矩阵、函数句柄、面向对象实例,每一种都有自己的序列化规则和坑。

5.1 结构体与元胞数组

结构体在 .mat 里被存成“字段名数组 + 字段值数组”的形式。如果结构体数组的各个元素字段不一致(MATLAB 允许这种情况,虽然不推荐),保存时会被拆成多个字段组,读取时可能得到意料之外的结构。元胞数组则被存成“每个元素的独立记录”,因此元胞数组的读取开销通常比同尺寸数值数组大。

一个实用建议是:在保存前用 struct2table 把规整的结构体数组转成 table,或者用 cell2mat 把同构元胞转成数值数组。这能显著减小文件体积、加快读写。

5.2 稀疏矩阵的存储优势

稀疏矩阵是 .mat 格式里少数“专门优化过”的类型。它只存非零元素的行索引、列索引和值,加上维度信息。对于一个 10000×10000、非零率 0.1% 的矩阵,稠密存储需要约 800 MB(双精度),稀疏存储只需要约 1.6 MB 量级。这个差距在保存和加载时都能明显感受到。

本文评述:笔者认为,稀疏矩阵的序列化是“格式感知数据”的一个好例子。同一个数学对象,用不同的存储策略,代价可以差几个数量级。这提醒我们:在 save 之前,先问一句“我的数据真的需要稠密存储吗”,往往比调任何参数都有效。

5.3 对象与函数句柄:能存什么,不能存什么

MATLAB 的面向对象实例可以保存,但保存的是“属性值的快照”,不是对象的行为。类定义本身不在 .mat 文件里。如果读取时类定义不存在或已改变,MATLAB 会尝试用当前类定义还原,可能失败或产生不一致的对象。函数句柄的保存更微妙:匿名函数句柄保存的是其捕获的工作区变量,如果捕获了很大的数据,文件会意外变大。

% 匿名函数句柄可能隐式捕获大变量
bigData = rand(1e7, 1);
f = @(x) x + mean(bigData);   % f 捕获了 bigData
save('func.mat', 'f');         % 文件会包含 bigData 的副本

% 更可控的写法:显式传参,避免隐式捕获
g = @(x, mu) x + mu;
mu = mean(bigData);
save('func2.mat', 'g', 'mu');

工程上的原则是:保存数据,而不是保存计算逻辑。函数句柄、对象实例这类“带行为”的东西,尽量在读取后重新构造,而不是依赖 .mat 里的快照。这样代码的可维护性和可移植性都会更好。

6. 大文件工程:分块、压缩、内存映射与并行 I/O

当工作区数据从 MB 级进入 GB 甚至 TB 级,save / load 的默认行为就不再适用。这一节讨论几种工程上常用的应对策略。

6.1 分块存储的设计

把一个大数组拆成多个 .mat 文件,是最朴素也最可靠的做法。关键决策是“按什么维度切”。对于时间序列,按时间窗口切;对于图像集合,按样本切;对于矩阵,按行或列块切。切分后要维护一个索引文件,记录每块的变量名、维度范围、文件路径。

% 分块保存大矩阵
function chunkedSave(data, outDir, chunkRows)
    if ~exist(outDir, 'dir'), mkdir(outDir); end
    [nRows, nCols] = size(data);
    idx = struct('file', {}, 'rowRange', {}, 'colRange', {});
    k = 0;
    for r = 1:chunkRows:nRows
        k = k + 1;
        rEnd = min(r + chunkRows - 1, nRows);
        fname = fullfile(outDir, sprintf('chunk_%04d.mat', k));
        block = data(r:rEnd, :);
        save(fname, 'block', '-v7.3');
        idx(k).file = fname;
        idx(k).rowRange = [r, rEnd];
        idx(k).colRange = [1, nCols];
    end
    save(fullfile(outDir, 'index.mat'), 'idx');
end

这种方案的优点是实现简单、可并行、单块失败不影响全局。缺点是需要自己管理索引,且跨块操作(如按列聚合)需要额外逻辑。本文评述:笔者认为,分块存储的价值不仅在于绕开大小限制,更在于它把“一个大任务”拆成了“多个可独立处理的小任务”,天然适合并行和断点续算。在数据工程里,这往往比单纯追求“单文件更大”更有价值。

6.2 压缩的取舍

v7 的 zlib 压缩和 v7.3 的 HDF5 压缩,都是“时间换空间”。压缩率取决于数据的熵:浮点噪声几乎不可压缩,整数索引、稀疏结构、重复模式压缩率很高。在写入密集的场景,压缩可能成为瓶颈。一个实用的判断方法是:先用 save 存一次带压缩、一次不带压缩,比较文件大小和耗时,再决定。

数据类型 压缩率(模拟) 建议
随机双精度浮点 接近 1.0(无压缩) 关闭压缩
整数索引数组 2–5 倍 开启压缩
稀疏矩阵 3–10 倍 开启压缩
重复模式文本 5–20 倍 开启压缩

上表压缩率为模拟数据,用于说明趋势,实际值需以具体数据实测为准。来源:笔者基于 zlib 压缩特性的整合估算。

6.3 并行 I/O 与内存映射

在有多核、多节点的集群上,可以用 parfor 并行保存多个分块文件。但要注意,多个 worker 同时写同一个 .mat 文件是不安全的,必须每个 worker 写自己的文件。内存映射(memory-mapped file)是另一条路:用 memmapfile 把文件映射成内存数组,读写像操作普通数组一样。它适合“固定格式、频繁随机访问”的场景,但不适合变长记录。

本文评述:笔者认为,大文件工程的本质矛盾是“内存有限、数据无限”。所有策略——分块、压缩、懒加载、内存映射——都是在不同维度上缓解这个矛盾。没有银弹,只有权衡。选择哪种,取决于你的访问模式:是顺序扫描多,还是随机读取多;是写一次读多次,还是频繁更新。先搞清楚访问模式,再选工具,顺序不能反。

7. 跨语言互操作:Python、Julia、R 与 HDF5 生态

现代科研和工程很少只用一种语言。MATLAB 存的数据,经常需要被 Python 脚本、Julia 计算内核或 R 统计包读取。.mat 的跨语言能力,直接决定了它在多语言流水线里的位置。

7.1 Python 读 .mat:scipy.io 与 h5py

Python 生态里读 .mat 有两条主流路径。一条是 scipy.io.loadmat,它支持 v4、v6、v7,但对 v7.3 无能为力。另一条是 h5py,它把 v7.3 文件当普通 HDF5 读,能处理大文件,但需要自己理解 MATLAB 的数据布局约定。

# 路径一:scipy.io(v7 及以下)
from scipy.io import loadmat
data = loadmat('data_v7.mat')
signal = data['signal']          # numpy 数组

# 路径二:h5py(v7.3)
import h5py
import numpy as np
with h5py.File('data_v73.mat', 'r') as f:
    signal = np.array(f['signal']).T   # 注意转置!
    # MATLAB 列主序,HDF5 行主序,读出来需要转置

这里有一个非常关键的细节:MATLAB 数组是列主序(column-major),HDF5 和 NumPy 默认是行主序(row-major)。用 h5py 读出来的数组,维度顺序和 MATLAB 里看到的相反,需要转置。这是跨语言读 .mat 最常见的 bug 来源之一。

本文评述:笔者认为,列主序/行主序的差异,是“格式契约”里最容易被忽视的一条。它不会报错,只会让结果悄悄错掉。工程上的应对是:在跨语言接口的文档里明确写出维度约定,并在单元测试里用非对称矩阵(如 3×5)验证转置是否正确。用方阵测试是发现不了这个问题的。

7.2 Julia 与 R 的读取方案

Julia 生态里,MAT.jl 提供了较完整的 .mat 读写支持,包括 v7.3。R 生态里,R.matlab 包可以读写 v5/v6/v7 格式,v7.3 支持有限,通常建议用 rhdf5 直接读 HDF5。这些工具的存在,让 .mat 在统计计算和数值计算社区之间有了桥梁。

7.3 互操作的最佳实践

  1. 如果数据要被 Python 读,优先用 -v7.3,并接受 h5py 的转置约定。
  2. 避免在 .mat 里存 MATLAB 特有的复杂类型(table、categorical、datetime),跨语言读取时容易出问题。
  3. 变量名用 ASCII,避免中文或特殊字符,减少编码问题。
  4. 在文件里附一个 README 变量,用字符串说明各变量的含义、维度、单位。

8. 云端与版本化:对象存储、DVC 与可复现研究

数据一旦离开本地磁盘,进入云端对象存储或版本控制系统,.mat 文件的治理问题就浮现出来。

8.1 对象存储的适配

S3、OSS、GCS 这类对象存储,适合存“写一次、读多次”的大文件。它们不支持文件内的随机写入,所以 matfile 的部分写入功能在对象存储上无法直接使用。工程上的做法是:本地生成完整 .mat,再整体上传;或者用分块文件,每块独立上传。

上传后要记录校验和(如 MD5、SHA-256),因为网络传输可能引入损坏。MATLAB 里可以用 Simulink.getFileChecksum 或调用系统命令计算。下载后比对校验和,是保证数据完整性的基本操作。

8.2 数据版本控制

Git 不适合直接管理大的 .mat 文件,因为二进制文件无法有效 diff,且会让仓库膨胀。DVC(Data Version Control)是常用的替代方案:它把大文件存在远端(S3、NAS 等),Git 里只存一个指向文件的元数据指针。这样既能版本化数据,又不污染代码仓库。

# DVC 基本流程
dvc init
dvc add data/experiment_01.mat
git add data/experiment_01.mat.dvc .gitignore
git commit -m "add experiment 01 data"
dvc push   # 上传到远端存储

# 其他人拉取
git pull
dvc pull   # 从远端下载 .mat 文件

本文评述:笔者认为,DVC 这类工具解决的不只是“文件太大”的问题,更是“数据与代码如何对应”的问题。在可复现研究里,一个常见的失败模式是:代码更新了,但数据还是旧的,结果对不上。把数据版本和代码版本绑定,是避免这类问题的有效手段。.mat 文件作为数据载体,天然适合纳入这种版本化管理。

8.3 可复现性的元数据设计

一个负责任的 .mat 文件,应该自带足够的元信息,让未来的人能理解它。建议在保存时额外写入一个 meta 结构体,包含:创建时间、MATLAB 版本、数据来源、预处理步骤、变量说明、单位、联系人。这些信息占不了多少空间,但能极大降低后续使用的沟通成本。

meta = struct();
meta.createdAt = datestr(now, 'yyyy-mm-dd HH:MM:SS');
meta.matlabVersion = version;
meta.source = 'sensor_array_A, 2024-03 campaign';
meta.preprocessing = 'bandpass 0.5-40 Hz, notch 50 Hz';
meta.variables = {'signal: [nSamples x nChannels], uV', ...
                  'fs: scalar, Hz', ...
                  'channelNames: 1 x nChannels cell'};
meta.contact = 'lab@example.org';
save('experiment.mat', 'signal', 'fs', 'channelNames', 'meta', '-v7.3');

9. 前沿预判:MAT 格式在异构计算时代的位置

讨论完工程实践,有必要抬头看看趋势。.mat 格式诞生于单机、单语言、以数值数组为中心的时代。今天的环境已经大不相同:GPU 计算、分布式集群、多语言流水线、云原生部署。这些变化会如何影响 .mat 的角色?

9.1 与列式格式的竞争

Parquet、Arrow、Zarr 这些为大数据和异构计算设计的格式,正在科学数据领域快速普及。Parquet 适合表格型数据,Arrow 适合内存中的列式表示,Zarr 适合分块的 N 维数组。它们大多有成熟的 Python/R/Julia 支持,且与云存储、并行计算框架集成良好。相比之下,.mat 的优势在于与 MATLAB 生态的深度绑定,以及“一个文件装下整个工作区”的便利。

本文评述:笔者认为,.mat 不会消失,但它的定位会从“通用数据交换格式”收缩为“MATLAB 工作区快照格式”。跨语言、跨团队的数据交换,会越来越多地走 Parquet/Arrow/Zarr 这条路。.mat 的 v7.3 选择 HDF5 作为底层,某种程度上是在为这个收缩做准备——至少数据还能被其他工具读出来。

9.2 GPU 与分布式数据的持久化

当数据常驻 GPU 显存时,save 需要先把数据拷回主机内存,再写磁盘。这个往返在大数据量下可能成为瓶颈。一些工具箱(如 Parallel Computing Toolbox)提供了 gather 后再保存的路径,但本质上没有改变“显存→内存→磁盘”的链路。分布式数组(distributed array)的保存,则需要各 worker 协同写出分块文件。

一个值得关注的方向是“计算靠近数据”:与其把数据从存储拉到计算节点,不如把计算调度到数据所在的节点。这对 .mat 文件意味着,未来的持久化方案可能需要支持“在存储端直接执行部分计算”,而不只是简单的读写。这已经超出了当前 save/load 的范畴,但值得持续观察。

9.3 长期归档的格式选择

对于需要保存十年以上的科研数据,格式的长期可读性比读写性能更重要。HDF5 有独立的非营利组织维护规范,有 C 库的长期支持,这比私有二进制格式更让人放心。-v7.3 在这方面比 -v7 更有优势。但即使选了 HDF5,也要注意:MATLAB 在 HDF5 上加了特定的属性约定,这些约定本身没有公开的正式规范,未来版本可能变化。最稳妥的长期归档,是把数据同时导出为“纯 HDF5 + 一份说明文档”,减少对 MATLAB 特定约定的依赖。

10. 工程实践清单与常见陷阱

把前面各节的要点浓缩成一份可执行的清单,方便在实际项目中对照使用。

10.1 保存前检查清单

  1. 用 whos 确认要保存的变量,排除临时变量(如循环计数器、绘图句柄)。
  2. 估算总大小,超过 1.5 GB 时考虑 -v7.3。
  3. 检查是否有匿名函数句柄捕获了大变量。
  4. 检查是否有 MATLAB 特有类型需要跨语言读取。
  5. 决定是否写入 meta 元信息结构体。
  6. 确认输出路径存在且有写权限。

10.2 常见陷阱对照表

现象 可能原因 应对
保存报“变量过大” v7 的 2 GB 上限 改用 -v7.3
Python 读出的数组维度反了 🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
黔ICP备19010680号-1  |  邮箱:six528528@163.com
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷