最快的Matlab文件阅读?

我的MATLAB程序正在读取大约7米长的文件,并浪费了太多时间在I / O上。 我知道每行都被格式化为两个整数,但我不知道他们占用了多less个字符。 str2num是死一般的,应该用什么matlab函数呢?

Catch:我必须一次一行地操作每一行,而不存储整个文件内存,所以没有读取整个matrix的命令都在桌面上。

fid = fopen('file.txt'); tline = fgetl(fid); while ischar(tline) nums = str2num(tline); %do stuff with nums tline = fgetl(fid); end fclose(fid); 

问题陈述

这是一个共同的斗争,没有什么比testing回答。 这是我的假设:

  1. 格式良好的ASCII文件,包含两列数字。 没有标题,没有不一致的线条等

  2. 该方法必须扩展到读取文件太大,不能被包含在内存中(虽然我的耐心是有限的,所以我的testing文件只有50万行)。

  3. 实际操作(OP所谓的“做什么东西”)必须一次执行一行,不能被vector化。

讨论

考虑到这一点,答案和评论似乎在三个方面提高了效率:

  • 大批量阅读文件
  • 更有效地执行string到数字的转换(通过批处理或使用更好的function)
  • 使实际处理更有效率(我已经通过上面的规则3排除了)。

结果

我编写了一个快速的脚本来testing这些主题的6个变化的摄取速度(和结果的一致性)。 结果是:

  • 初始代码。 68.23秒 582582检查
  • 使用sscanf,每行一次。 27.20秒 582582检查
  • 大量使用fscanf。 8.93秒 582582检查
  • 大批量使用文本扫描。 8.79秒 582582检查
  • 将大批量读入内存,然后sscanf。 8.15秒 582582检查
  • 在单行上使用java单行文件读取器和sscanf。 63.56秒 582582检查
  • 使用java单品令牌扫描器。 81.19秒 582582检查
  • 完全批量的操作(不符合)。 1.02秒 508680检查(违反规则3)

概要

超过一半的原始时间(68-> 27秒)在str2num调用中效率低下,可以通过切换sscanf来消除。

大约2/3的剩余时间(27 – > 8秒)可以通过使用大批量的文件读取和string到数字转换来减less。

如果我们愿意违反原文中的第三条规则,则可以通过切换到全数字处理来减less另外7/8的时间。 但是,有些algorithm不适合这个,所以我们不要pipe它。 (不是“检查”值不匹配的最后一项。)

最后,在这个响应中,与之前的编辑相反,通过切换可用的cachingJava,单行读取器,没有任何节省。 事实上,这个解决scheme比使用原生阅读器的单行结果慢2-3倍。 (63比27秒)。

下面介绍了上述所有解决scheme的示例代码。


示例代码

 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Create a test file cd(tempdir); fName = 'demo_file.txt'; fid = fopen(fName,'w'); for ixLoop = 1:5 d = randi(1e6, 1e5,2); fprintf(fid, '%d, %d \n',d); end fclose(fid); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Initial code CHECK = 0; tic; fid = fopen('demo_file.txt'); tline = fgetl(fid); while ischar(tline) nums = str2num(tline); CHECK = round((CHECK + mean(nums) ) /2); tline = fgetl(fid); end fclose(fid); t = toc; fprintf(1,'Initial code. %3.2f sec. %d check \n', t, CHECK); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Using sscanf, once per line CHECK = 0; tic; fid = fopen('demo_file.txt'); tline = fgetl(fid); while ischar(tline) nums = sscanf(tline,'%d, %d'); CHECK = round((CHECK + mean(nums) ) /2); tline = fgetl(fid); end fclose(fid); t = toc; fprintf(1,'Using sscanf, once per line. %3.2f sec. %d check \n', t, CHECK); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Using fscanf in large batches CHECK = 0; tic; bufferSize = 1e4; fid = fopen('demo_file.txt'); scannedData = reshape(fscanf(fid, '%d, %d', bufferSize),2,[])' ; while ~isempty(scannedData) for ix = 1:size(scannedData,1) nums = scannedData(ix,:); CHECK = round((CHECK + mean(nums) ) /2); end scannedData = reshape(fscanf(fid, '%d, %d', bufferSize),2,[])' ; end fclose(fid); t = toc; fprintf(1,'Using fscanf in large batches. %3.2f sec. %d check \n', t, CHECK); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Using textscan in large batches CHECK = 0; tic; bufferSize = 1e4; fid = fopen('demo_file.txt'); scannedData = textscan(fid, '%d, %d \n', bufferSize) ; while ~isempty(scannedData{1}) for ix = 1:size(scannedData{1},1) nums = [scannedData{1}(ix) scannedData{2}(ix)]; CHECK = round((CHECK + mean(nums) ) /2); end scannedData = textscan(fid, '%d, %d \n', bufferSize) ; end fclose(fid); t = toc; fprintf(1,'Using textscan in large batches. %3.2f sec. %d check \n', t, CHECK); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Reading in large batches into memory, incrementing to end-of-line, sscanf CHECK = 0; tic; fid = fopen('demo_file.txt'); bufferSize = 1e4; eol = sprintf('\n'); dataBatch = fread(fid,bufferSize,'uint8=>char')'; dataIncrement = fread(fid,1,'uint8=>char'); while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid) dataIncrement(end+1) = fread(fid,1,'uint8=>char'); %This can be slightly optimized end data = [dataBatch dataIncrement]; while ~isempty(data) scannedData = reshape(sscanf(data,'%d, %d'),2,[])'; for ix = 1:size(scannedData,1) nums = scannedData(ix,:); CHECK = round((CHECK + mean(nums) ) /2); end dataBatch = fread(fid,bufferSize,'uint8=>char')'; dataIncrement = fread(fid,1,'uint8=>char'); while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid) dataIncrement(end+1) = fread(fid,1,'uint8=>char');%This can be slightly optimized end data = [dataBatch dataIncrement]; end fclose(fid); t = toc; fprintf(1,'Reading large batches into memory, then sscanf. %3.2f sec. %d check \n', t, CHECK); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Using Java single line readers + sscanf CHECK = 0; tic; bufferSize = 1e4; reader = java.io.LineNumberReader(java.io.FileReader('demo_file.txt'),bufferSize ); tline = char(reader.readLine()); while ~isempty(tline) nums = sscanf(tline,'%d, %d'); CHECK = round((CHECK + mean(nums) ) /2); tline = char(reader.readLine()); end reader.close(); t = toc; fprintf(1,'Using java single line file reader and sscanf on single lines. %3.2f sec. %d check \n', t, CHECK); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Using Java scanner for file reading and string conversion CHECK = 0; tic; jFile = java.io.File('demo_file.txt'); scanner = java.util.Scanner(jFile); scanner.useDelimiter('[\s\,\n\r]+'); while scanner.hasNextInt() nums = [scanner.nextInt() scanner.nextInt()]; CHECK = round((CHECK + mean(nums) ) /2); end scanner.close(); t = toc; fprintf(1,'Using java single item token scanner. %3.2f sec. %d check \n', t, CHECK); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Reading in large batches into memory, vectorized operations (non-compliant solution) CHECK = 0; tic; fid = fopen('demo_file.txt'); bufferSize = 1e4; eol = sprintf('\n'); dataBatch = fread(fid,bufferSize,'uint8=>char')'; dataIncrement = fread(fid,1,'uint8=>char'); while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid) dataIncrement(end+1) = fread(fid,1,'uint8=>char'); %This can be slightly optimized end data = [dataBatch dataIncrement]; while ~isempty(data) scannedData = reshape(sscanf(data,'%d, %d'),2,[])'; CHECK = round((CHECK + mean(scannedData(:)) ) /2); dataBatch = fread(fid,bufferSize,'uint8=>char')'; dataIncrement = fread(fid,1,'uint8=>char'); while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid) dataIncrement(end+1) = fread(fid,1,'uint8=>char');%This can be slightly optimized end data = [dataBatch dataIncrement]; end fclose(fid); t = toc; fprintf(1,'Fully batched operations. %3.2f sec. %d check \n', t, CHECK); 

(原始答案)

为了扩大Ben所提出的观点,如果您逐行读取这些文件,您的瓶颈将始终是文件I / O。

我明白,有时你不能把整个文件放到内存中。 我通常阅读大量的字符(1e5,1e6或其附近,取决于系统的内存)。 然后,我要么读取额外的单个字符(或单个字符后退)来获得一个循环的行数,然后运行您的stringparsing(例如sscanf)。

那么如果你想要的话,你可以一次处理一行所产生的大matrix,然后重复这个过程,直到你读到文件的结尾。

这有点乏味,但并不难。 我通常看到比单线阅读器速度提高了90%以上。


(使用Java批处理行读取器的可怕想法被删除了)

即使你不能将整个文件放在内存中,也应该使用matrix读取function来读取大批量的文件。

也许你甚至可以使用vector操作进行一些数据处理,这会加快速度。

我使用memmapfile()有很好的结果(快速memmapfile() 。 这最大限度地减less了内存数据复制的数量,并利用内核的IO缓冲。 你需要足够的空闲地址空间(虽然不是实际的空闲内存)来映射整个文件,并有足够的空闲内存来保存输出variables(显然!)

下面的示例代码将文本文件读入int32types的两列matrixdata

 fname = 'file.txt'; fstats = dir(fname); % Map the file as one long character string m = memmapfile(fname, 'Format', {'uint8' [ 1 fstats.bytes] 'asUint8'}); textdata = char(m.Data(1).asUint8); % Use textscan() to parse the string and convert to an int32 matrix data = textscan(textdata, '%d %d', 'CollectOutput', 1); data = data{:}; % Tidy up! clear('m') 

您可能需要摆弄textscan()参数才能得到您想要的内容 – 请参阅在线文档。

我发现MATLAB读取csv文件的速度明显快于文本文件,所以如果可以使用其他软件将文本文件转换为csv,可能会显着加快Matlab的运行速度。