NOTE: 本问题涉及 2011 年旧 MATLAB 版本 (R2009a) 中观察到的问题。根据 2016 年 7 月的以下更新,MATLAB 中的问题/错误似乎不再存在(使用 R2016a 进行测试;向下滚动到问题末尾以查看更新)。
我正在使用 MATLAB R2009b,需要编写一个更大的脚本,将更大的 .zip 文件集的内容转换为 v7.3 mat 文件(带有底层 HDF5 数据模型)。读书是可以的。问题在于保存。而且实际上也没有问题。我的文件使用以下方式保存得很好save命令。
我的问题更有意义:为什么我在 MATLAB 中观察到以下令人惊讶的(对我来说)行为?
让我们总体上看一下我的问题。在当前的测试场景中,我将生成一个输出:A -v7.3 mat 文件。该 .mat 文件将包含 40blocks作为个体变量。每个变量将被命名为从 1 到 40 的“block_NNN”,并将包含一个带有字段的结构frames and blockNo. Field frames包含一个 480x240x65 的 uint8 图像数据序列(这里只是使用生成的随机数据randi)。场地blockNo包含块号。
Remark: 在真实的脚本中(我尚未完成),我将总共执行上述 370 次,总共转换 108GB 的原始数据。这就是为什么我担心以下问题。
无论如何,首先我定义一些通用变量:
% some sizes for dummy data and loops:
num_blockCount = 40;
num_blockLength = 65;
num_frameHeight = 480;
num_frameWidth = 240;
然后,我生成一些虚拟代码,其形状和大小与实际原始数据相同:
% generate empty struct:
stu_data2disk = struct();
% loop over blocks:
for num_k = 1:num_blockCount
% generate block-name:
temp_str_blockName = sprintf('block_%03u', num_k);
% generate temp struct for current block:
temp_stu_value = struct();
temp_stu_value.frames = randi( ...
[0 255], ...
[num_frameHeight num_frameWidth num_blockLength], ...
'uint8' ...
);
temp_stu_value.blockNo = num_k;
% using dynamic field names:
stu_data2disk.(sprintf('block_%03u', num_k)) = temp_stu_value;
end
我现在将所有随机测试数据放在一个结构中Stu_data2disk。现在我想使用两种可能的方法之一保存数据。
让我们先尝试一下简单的:
% save data (simple):
disp('Save data the simple way:')
tic;
save converted.mat -struct stu_data2disk -v7.3;
toc;
文件写入没有问题(286MB)。输出是:
Save data the simple way:
Elapsed time is 14.004449 seconds.
好的 - 然后我想起来我想在 40 个区块上遵循保存程序。因此,我不是上面那样循环遍历块并按顺序附加它们:
% save to file, using append:
disp('Save data using -append:')
tic;
for num_k = 1:num_blockCount
% generate block-name:
temp_str_blockName = sprintf('block_%03u', num_k);
temp_str_appendToggle = '';
if (num_k > 1)
temp_str_appendToggle = '-append';
end
% generate save command:
temp_str_saveCommand = [ ...
'save ', ...
'converted_append.mat ', ...
'-struct stu_data2disk ', temp_str_blockName, ' '...
temp_str_appendToggle, ' ', ...
'-v7.3', ...
';' ...
];
% evaluate save command:
eval(temp_str_saveCommand);
end
toc;
文件再次保存得很好(286MB)。输出是:
Save data using -append:
Elapsed time is 0.956968 seconds.
有趣的是,追加方法更快吗?我的问题是为什么?
输出来自dir converted*.mat
:
09-02-2011 20:38 300,236,392 converted.mat
09-02-2011 20:37 300,264,316 converted_append.mat
2 File(s) 600,500,708 bytes
这些文件的大小不相同。并进行了测试fc在 Windows 7 中揭示了……许多二进制差异。也许数据发生了一些变化——因此这没有告诉我们什么。
有人知道这里发生了什么吗?附加文件是否可能使用更优化的数据结构?或者也许 Windows 已经缓存了该文件并且可以更快地访问它?
我也尝试读取这两个文件。如果没有在此处提供数字,则附加版本会更快一些(但从长远来看可能意味着某些事情)。
[EDIT]:我只是尝试使用无格式标志(在我的系统上默认为 -v7),并且不再有太大区别:
Save data the simple way (-v7):
Elapsed time is 13.092084 seconds.
Save data using -append (-v7):
Elapsed time is 14.345314 seconds.
[EDIT]: 我纠正了上面的错误。之前我提到统计数据是针对 -v6 的,但我错了。我刚刚删除了格式标志并假设默认值是 -v6,但实际上它是 -v7。
我使用安德鲁的精细框架为系统上的所有格式创建了新的测试统计数据(所有格式都用于相同的随机测试数据,现在从文件中读取):
15:15:51.422: Testing speed, format=-v6, R2009b on PCWIN, arch=x86, os=Microsoft Windows 7 Professional 6.1.7600 N/A Build 7600
15:16:00.829: Save the simple way: 0.358 sec
15:16:01.188: Save using multiple append: 7.432 sec
15:16:08.614: Save using one big append: 1.161 sec
15:16:24.659: Testing speed, format=-v7, R2009b on PCWIN, arch=x86, os=Microsoft Windows 7 Professional 6.1.7600 N/A Build 7600
15:16:33.442: Save the simple way: 12.884 sec
15:16:46.329: Save using multiple append: 14.442 sec
15:17:00.775: Save using one big append: 13.390 sec
15:17:31.579: Testing speed, format=-v7.3, R2009b on PCWIN, arch=x86, os=Microsoft Windows 7 Professional 6.1.7600 N/A Build 7600
15:17:40.690: Save the simple way: 13.751 sec
15:17:54.434: Save using multiple append: 3.970 sec
15:17:58.412: Save using one big append: 6.138 sec
以及文件的大小:
10-02-2011 15:16 299,528,768 converted_format-v6.mat
10-02-2011 15:16 299,528,768 converted_append_format-v6.mat
10-02-2011 15:16 299,528,832 converted_append_batch_format-v6.mat
10-02-2011 15:16 299,894,027 converted_format-v7.mat
10-02-2011 15:17 299,894,027 converted_append_format-v7.mat
10-02-2011 15:17 299,894,075 converted_append_batch_format-v7.mat
10-02-2011 15:17 300,236,392 converted_format-v7.3.mat
10-02-2011 15:17 300,264,316 converted_append_format-v7.3.mat
10-02-2011 15:18 300,101,800 converted_append_batch_format-v7.3.mat
9 File(s) 2,698,871,005 bytes
因此 -v6 似乎是写入速度最快的。文件大小也没有太大差异。据我所知,HDF5 确实内置了一些基本的膨胀方法。
嗯,可能对底层 HDF5 写入函数进行了一些优化?
目前我仍然认为一些底层的基本 HDF5-write 函数针对添加进行了优化datasets到 HDF5 文件(这就是向 -7.3 文件添加新变量时发生的情况)。我相信我在某处读过 HDF5 应该以这种方式优化......尽管不能确定。
其他需要注意的细节:
正如我们在下面安德鲁的回答中看到的那样,这种行为是非常系统性的。至于是否在函数的本地范围或 m 脚本的“全局”范围内运行这些东西似乎也非常重要。我的第一个结果来自 m 脚本,其中文件被写入当前目录。我仍然只能在 m 脚本中重现 -7.3 的 1 秒写入。函数调用显然增加了一些开销。
2016 年 7 月更新:
我再次发现了这一点,并认为我可以使用目前可用的最新 MATLAB 对其进行测试。使用 Windows 7 x64 上的 MATLAB R2016a,问题似乎已得到解决:
14:04:06.277: Testing speed, imax=255, R2016a on PCWIN64, arch=AMD64, 16 GB, os=Microsoft Windows 7 Enterprise Version 6.1 (Build 7601: Service Pack 1)
14:04:10.600: basic -v7.3: 7.599 sec 5.261 GB used
14:04:18.229: basic -v7.3: 7.894 sec 5.383 GB used
14:04:26.154: basic -v7.3: 7.909 sec 5.457 GB used
14:04:34.096: basic -v7.3: 7.919 sec 5.498 GB used
14:04:42.048: basic -v7.3: 7.886 sec 5.516 GB used 286 MB file 7.841 sec mean
14:04:50.581: multiappend -v7.3: 7.928 sec 5.819 GB used
14:04:58.544: multiappend -v7.3: 7.905 sec 5.834 GB used
14:05:06.485: multiappend -v7.3: 8.013 sec 5.844 GB used
14:05:14.542: multiappend -v7.3: 8.591 sec 5.860 GB used
14:05:23.168: multiappend -v7.3: 8.059 sec 5.868 GB used 286 MB file 8.099 sec mean
14:05:31.913: bigappend -v7.3: 7.727 sec 5.837 GB used
14:05:39.676: bigappend -v7.3: 7.740 sec 5.879 GB used
14:05:47.453: bigappend -v7.3: 7.645 sec 5.884 GB used
14:05:55.133: bigappend -v7.3: 7.656 sec 5.877 GB used
14:06:02.824: bigappend -v7.3: 7.963 sec 5.871 GB used 286 MB file 7.746 sec mean
这是用 Andrew Janke 测试的reproMatfileAppendSpeedup
下面接受的答案中的函数(格式 7.3 的 5 遍)。现在,-append
对于单次保存来说同样很慢,或者更慢 - 正如它应该的那样。也许这是 R2009a 中使用的 HDF5 驱动程序的早期版本的问题。