Labfans是一个针对大学生、工程师和科研工作者的技术社区。 | 论坛首页 | 联系我们(Contact Us) |
![]() |
![]() |
#1 |
高级会员
注册日期: 2019-11-21
帖子: 3,006
声望力: 66 ![]() |
![]()
注意: 此问题处理的是2011年使用旧版MATLAB(R2009a)时发现的问题。根据2016年7月以下的更新,MATLAB中的问题/错误似乎不再存在(已通过R2016a测试;向下滚动至问题末尾以查看更新)。
我使用的是MATLAB R2009b,我需要编写一个较大的脚本,将较大的.zip文件集的内容转换为v7.3 mat文件(具有基础HDF5-datamodel)。读书还可以。问题在于保存。而且实际上没有问题。使用save命令可以很好地保存我的文件。 我的问题更多地是在意义上:为什么我在MATLAB中观察到以下令人惊讶的行为(对我而言)? 让我们大致看一下我的问题。在当前的测试场景中,我将生成一个输出:-v7.3 mat-file。该.mat文件将包含40 个块作为单个变量。每个变量将从1到40命名为“ block_NNN”,并将包含带有字段frame和blockNo的结构。视场帧包含480x240x65的uint8图像数据序列(此处仅是使用randi生成的随机数据)。字段blockNo包含块号。 备注: 在真实的脚本中(我尚未完成),我将总共执行370次以上操作,总共转换了108GB的原始数据。这就是为什么我关注以下内容。 无论如何,首先我定义一些常规变量: %虚拟数据和循环的一些大小:num_blockCount = 40;num_blockLength = 65;num_frameHeight = 480;num_frameWidth = 240;然后,我生成一些伪代码,其形状和大小与实际的原始数据相同: %生成空结构:stu_data2disk = struct();%遍历块:对于num_k = 1:num_blockCount %生成块名: temp_str_blockName = sprintf('block_%03u',num_k); %为当前块生成温度结构: temp_stu_value = struct(); temp_stu_value.frames = randi(... [0 255],... [num_frameHeight num_frameWidth num_blockLength],... 'uint8'... ); temp_stu_value.blockNo = num_k; %使用动态字段名称: stu_data2disk。(sprintf('block_%03u',num_k))= temp_stu_value;结束现在,我所有的随机测试数据都放在stu_data2disk结构中。现在,我想使用两种可能的方法之一保存数据。 让我们先尝试简单的一个: %保存数据(简单):disp('以简单的方式保存数据:')抽动保存convert.mat -struct stu_data2disk -v7.3;托克写入文件没有问题(286MB)。输出为: 保存数据的简单方法是:经过的时间是14.004449秒。好的-然后我想起了要对40个块执行保存过程的过程。因此,代替上面的内容,我循环遍历这些块并按顺序附加它们: %使用追加保存到文件:disp('使用-append保存数据:')抽动对于num_k = 1:num_blockCount %生成块名: temp_str_blockName = sprintf('block_%03u',num_k); temp_str_appendToggle =''; 如果(num_k> 1) temp_str_appendToggle ='-附加'; 结束 %生成保存命令: temp_str_saveCommand = [... '保存 ', ... 'converted_append.mat',... '-struct stu_data2disk',temp_str_blockName,''... temp_str_appendToggle,'',... '-v7.3',... ';' ... ]; %评估保存命令: eval(temp_str_saveCommand);结束托克并再次很好地保存文件(286MB)。输出为: 使用-append保存数据:经过的时间是0.956968秒。有趣的是,追加方法要快得多? 我的问题是为什么? dir converted*.mat输出dir converted*.mat : 2011年9月2日20:38 300,236,392 convert.mat2011年9月2日20:37 300,264,316 convert_append.mat 2个文件600,500,708字节文件大小不同。在Windows 7中使用fc进行的测试揭示了……许多二进制差异。也许数据有些偏移-因此,这没有告诉我们。 有人知道这里发生了什么吗?附加文件是否使用了更为优化的数据结构?还是Windows缓存了文件并使访问速度更快? 我也尝试从这两个文件中读取数据。如果不在此处显示数字,则附加的版本会快一些(不过从长远来看可能意味着某些事情)。 [编辑] :我只是尝试不使用格式标志(我的系统上默认为-v7),并且没有太大区别了: 保存数据的简单方法(-v7):经过的时间是13.092084秒。使用-append(-v7)保存数据:经过的时间为14.345314秒。 [编辑] :我更正了以上错误。之前我提到过,统计数据是针对-v6的,但我弄错了。我刚刚删除了格式标志,并假定默认值为-v6,但实际上它是-v7。 我已使用安德鲁(Andrew)的优良框架为系统上的所有格式创建了新的测试统计信息(所有格式均针对相同的随机测试数据,现在可以从文件中读取): 15:15:51.422:测试速度,格式= -v6,PCWIN上的R2009b,arch = x86,os = Microsoft Windows 7 Professional 6.1.7600 N / A Build 760015:16:00.829:保存简单方法:0.358秒15:16:01.188:使用多次追加保存:7.432秒15:16:08.614:使用一个大附件进行保存:1.161秒15:16:24.659:测试速度,格式= -v7,PCWIN上的R2009b,arch = x86,os = Microsoft Windows 7 Professional 6.1.7600 N / A Build 760015:16:33.442:保存简单方法:12.884秒15:16:46.329:使用多次追加保存:14.442秒15:17:00.775:使用一个大附件进行保存:13.390秒15:17:31.579:测试速度,格式= -v7.3,PCWIN上的R2009b,arch = x86,os = Microsoft Windows 7 Professional 6.1.7600 N / A Build 760015:17:40.690:保存简单方法:13.751秒15:17:54.434:使用多次追加保存:3.970秒15:17:58.412:使用一个大附件进行保存:6.138秒以及文件的大小: 10-02-2011 15:16 299,528,768 convert_format-v6.mat10-02-2011 15:16 299,528,768 convert_append_format-v6.mat10-02-2011 15:16 299,528,832 convert_append_batch_format-v6.mat10-02-2011 15:16 299,894,027 convert_format-v7.mat10-02-2011 15:17 299,894,027 convert_append_format-v7.mat10-02-2011 15:17 299,894,075 convert_append_batch_format-v7.mat10-02-2011 15:17 300,236,392 convert_format-v7.3.mat10-02-2011 15:17 300,264,316 convert_append_format-v7.3.mat10-02-2011 15:18 300,101,800 convert_append_batch_format-v7.3.mat 9个文件2,698,871,005字节因此,-v6似乎是编写最快的。文件大小也没有太大差异。据我所知,HDF5确实内置了一些基本的充气方法。 嗯,可能是对底层HDF5写入功能的一些优化? 目前,我仍然认为一些基本的基本HDF5写入功能已针对将数据集添加到HDF5文件进行了优化(这是在将新变量添加到-7.3文件时发生的情况)。我相信我已经读过HDF5应该以这种方式进行优化的地方了……尽管不能确定。 其他要注意的细节: 正如我们在下面的安德鲁的答案中看到的,这种行为非常系统化。对于是否在函数的局部范围内或在m脚本的“全局”范围内运行这些内容,这似乎也非常重要。我的第一个结果来自一个m-script,该文件将文件写入当前目录。我仍然只能在m脚本中再现-7.3的1秒写操作。函数调用显然会增加一些开销。 2016年7月更新 : 我再次发现了这一点,并认为我可以使用当前可用的最新MATLAB对其进行测试。在Windows 7 x64上使用MATLAB R2016a似乎已解决了该问题: 14:04:06.277:在PCWIN64上测试速度,imax = 255,R2016a,arch = AMD64、16 GB,操作系统= Microsoft Windows 7 Enterprise版本6.1(内部版本7601:Service Pack 1)14:04:10.600:基本-v7.3:7.599秒(使用5.261 GB)14:04:18.229:基本-v7.3:7.894秒5.383 GB使用14:04:26.154:基本-v7.3:7.909秒使用5.457 GB14:04:34.096:基本-v7.3:7.919秒5.498 GB使用14:04:42.048:基本-v7.3:7.886秒5.516 GB使用的286 MB文件7.841秒的平均值14:04:50.581:多附加-v7.3:7.928秒5.819 GB使用14:04:58.544:多重附加-v7.3:使用7.905秒5.834 GB14:05:06.485:多附加-v7.3:使用8.013秒5.844 GB14:05:14.542:多附加-v7.3:使用8.591秒5.860 GB14:05:23.168:多重附加-v7.3:8.059秒5.868 GB使用了286 MB文件8.099秒的平均值14:05:31.913:bigappend -v7.3:7.727秒5.837 GB使用14:05:39.676:bigappend -v7.3:7.740秒5.879 GB已使用14:05:47.453:bigappend -v7.3:7.645秒5.884 GB已使用14:05:55.133:bigappend -v7.3:7.656秒5.877 GB已使用14:06:02.824:bigappend -v7.3:7.963秒5.871 GB使用了286 MB文件7.746秒的平均值这已在以下接受的答案中使用Andrew Janke的reproMatfileAppendSpeedup函数进行了测试(5遍,格式为7.3)。现在, -append到一次保存的速度同样慢或慢了。 R2009a中使用的HDF5驱动程序的早期构建可能是一个问题。 回答: 天啊。我可以复制。也尝试了单追加版本;它甚至更快。看起来像“附加”只是使基于HDF5的save()快了30倍。我没有解释,但我想分享我发现的内容。 我将您的测试代码包装在一个函数中,对其进行重构以使保存逻辑与测试数据结构无关,以便您可以在其他数据集上运行它,并添加了更多的诊断输出。 到处都看不到大的加速。在我的64位XP机器和32位Server 2003机器上,它是巨大的,在我的64位Windows 7机器上是巨大的,而在32位XP机器上不存在。 (尽管多次追加在Server 2003上造成了巨大损失。)在许多情况下,R2010b的运行速度较慢。也许HDF5会附加或保存对它的使用,只是会在较新的Windows版本上出现。 (XP x64实际上是Server 2003内核。)或者也许只是计算机配置上的不同。 XP x64机器上有一个快速RAID,而32位XP的RAM比其余的要少。您正在运行什么操作系统和体系结构?你也可以尝试这个再现吗? 19:36:40.289: Testing speed, format=-v7.3, R2009b on PCWIN64, arch=AMD64, os=Microsoft(R) Windows(R) XP Professional x64 Edition 5.2.3790 Service Pack 2 Build 3790 19:36:55.930: Save the simple way: 11.493 sec 19:37:07.415: Save using multiple append: 1.594 sec 19:37:09.009: Save using one big append: 0.424 sec 19:39:21.681: Testing speed, format=-v7.3, R2009b on PCWIN, arch=x86, os=Microsoft Windows XP Professional 5.1.2600 Service Pack 3 Build 2600 19:39:37.493: Save the simple way: 10.881 sec 19:39:48.368: Save using multiple append: 10.187 sec 19:39:58.556: Save using one big append: 11.956 sec 19:44:33.410: Testing speed, format=-v7.3, R2009b on PCWIN64, arch=AMD64, os=Microsoft Windows 7 Professional 6.1.7600 N/A Build 7600 19:44:50.789: Save the simple way: 14.354 sec 19:45:05.156: Save using multiple append: 6.321 sec 19:45:11.474: Save using one big append: 2.143 sec 20:03:37.907: Testing speed, format=-v7.3, R2009b on PCWIN, arch=x86, os=Microsoft(R) Windows(R) Server 2003, Enterprise Edition 5.2.3790 Service Pack 2 Build 3790 20:03:58.532: Save the simple way: 19.730 sec 20:04:18.252: Save using multiple append: 77.897 sec 20:05:36.160: Save using one big append: 0.630 sec 这看起来很大。如果它可以支撑其他数据集,我可能会在很多地方使用此技巧。 MathWorks也可能会提出一些建议。他们还能在普通保存或其他OS版本中使用快速追加技术吗? 这是自包含的复制功能。 function out = reproMatfileAppendSpeedup(nPasses, tests, imax, formats) %REPROMATFILEAPPENDSPEEDUP Show how -append makes v7.3 saves much faster % % Examples: % reproMatfileAppendSpeedup() % reproMatfileAppendSpeedup(2, [], 0, {'7.3','7','6'}); % low-entropy test if nargin < 1 || isempty(nPasses); nPasses = 1; end if nargin < 2 || isempty(tests); tests = {'basic','multiappend','bigappend'}; end if nargin < 3 || isempty(imax); imax = 255; end if nargin < 4 || isempty(formats); formats = '7.3'; end % -v7 and -v6 do not show the speedup tests = cellstr(tests); formats = cellstr(formats); fprintf('%s: Testing speed, imax=%d, R%s on %s\n',... timestamp, imax, version('-release'), systemDescription()); tempDir = setupTempDir(); testData = generateTestData(imax); testMap = struct('basic','saveSimple', 'multiappend','saveMultiAppend', 'bigappend','saveBigAppend'); for iFormat = 1:numel(formats) format = formats{iFormat}; formatFlag = ['-v' format]; %fprintf('%s: Format %s\n', timestamp, formatFlag); for iTest = 1:numel(tests) testName = tests{iTest}; saveFcn = testMap.(testName); te = NaN(1, nPasses); for iPass = 1:nPasses fprintf('%s: %-30s', timestamp, [testName ' ' formatFlag ':']); t0 = tic; matFile = fullfile(tempDir, sprintf('converted-%s-%s-%d.mat', testName, format, i)); feval(saveFcn, matFile, testData, formatFlag); te(iPass) = toc(t0); if iPass == nPasses fprintf('%7.3f sec %5.3f GB used %5.0f MB file %5.3f sec mean\n',... te(iPass), physicalMemoryUsed/(2^30), getfield(dir(matFile),'bytes')/(2^20), mean(te)); else fprintf('%7.3f sec %5.3f GB used\n', te(iPass), physicalMemoryUsed/(2^30)); end end % Verify data to make sure we are sane gotBack = load(matFile); gotBack = rmfield(gotBack, intersect({'dummy'}, fieldnames(gotBack))); if ~isequal(gotBack, testData) fprintf('ERROR: Loaded data differs from original for %s %s\n', formatFlag, testName); end end end % Clean up rmdir(tempDir, 's'); %% function saveSimple(file, data, formatFlag) save(file, '-struct', 'data', formatFlag); %% function out = physicalMemoryUsed() if ~ispc out = NaN; return; % memory() only works on Windows end [u,s] = memory(); out = s.PhysicalMemory.Total - s.PhysicalMemory.Available; %% function saveBigAppend(file, data, formatFlag) dummy = 0; save(file, 'dummy', formatFlag); fieldNames = fieldnames(data); save(file, '-struct', 'data', fieldNames{:}, '-append', formatFlag); %% function saveMultiAppend(file, data, formatFlag) fieldNames = fieldnames(data); for i = 1:numel(fieldNames) if (i > 1); appendFlag = '-append'; else; appendFlag = ''; end save(file, '-struct', 'data', fieldNames{i}, appendFlag, formatFlag); end %% function testData = generateTestData(imax) nBlocks = 40; blockSize = [65 480 240]; for i = 1:nBlocks testData.(sprintf('block_%03u', i)) = struct('blockNo',i,... 'frames', randi([0 imax], blockSize, 'uint8')); end %% function out = timestamp() %TIMESTAMP Showing timestamps to make sure it is not a tic/toc problem out = datestr(now, 'HH:MM:SS.FFF'); %% function out = systemDescription() if ispc platform = [system_dependent('getos'),' ',system_dependent('getwinsys')]; elseif ismac [fail, input] = unix('sw_vers'); if ~fail platform = strrep(input, 'ProductName:', ''); platform = strrep(platform, sprintf('\t'), ''); platform = strrep(platform, sprintf('\n'), ' '); platform = strrep(platform, 'ProductVersion:', ' Version: '); platform = strrep(platform, 'BuildVersion:', 'Build: '); else platform = system_dependent('getos'); end else platform = system_dependent('getos'); end arch = getenv('PROCESSOR_ARCHITEW6432'); if isempty(arch) arch = getenv('PROCESSOR_ARCHITECTURE'); end try [~,sysMem] = memory(); catch sysMem.PhysicalMemory.Total = NaN; end out = sprintf('%s, arch=%s, %.0f GB, os=%s',... computer, arch, sysMem.PhysicalMemory.Total/(2^30), platform); %% function out = setupTempDir() out = fullfile(tempdir, sprintf('%s - %s', mfilename, datestr(now, 'yyyymmdd-HHMMSS-FFF'))); mkdir(out); 编辑:我修改了repro函数,添加了多个迭代并将其参数化为randi生成器的保存样式,文件格式和imax。 我认为文件系统缓存是快速追加行为的重要因素。当我使用reproMatfileAppendSpeedup(20)连续运行一堆并在Process Explorer中查看系统信息时,其中大多数时间不到一秒钟,而物理内存使用量迅速增加了几GB。然后,每打十次,写入停顿并花费20或30秒,物理RAM的使用量缓慢下降到开始的位置。我认为这意味着Windows正在RAM中缓存大量写操作,而-append使得它更愿意这样做。但是对我来说,包括摊位在内的摊销时间仍然比基本节省要快得多。 顺便说一句,经过几个小时的多次传递之后,我很难重现原始的时间安排。 更多&回答... |
![]() |
![]() |