Matlab:读取大型二进制文件 parts/sequences 的最快方法

Matlab: fastest method of reading parts/sequences of a large binary file

我想从一个大的(大约 11 GB)二进制文件中读取部分内容。当前有效的解决方案是用 fread() 加载整个文件 ( raw_data ),然后裁剪出感兴趣的部分 ( data )。

问题: 是否有更快的方法读取文件的小部分(占总文件的 1-2%,部分顺序读取),给定二进制掩码(即 Matlab 中感兴趣的特定字节的逻辑索引)?具体如下。

我的具体案例说明:

事实证明,我对 fread-fseek 循环的最初想法比读取整个文件要慢得多(请参阅下面的伪代码)。分析显示 fread() 是最慢的(被调用超过一百万次对于这里的专家来说可能是显而易见的)。

我考虑的备选方案:memmapfile() [ref ] has no feasible read multiple small parts as far as I could find. The MappedTensor library might be the next thing I'd look into. Related but didn't help, just to link to article: 1, .

%open file
fi=fopen('data.bin');

%example read-skip data
f_reads = [20  10   6  20  40];  %read this number of bytes
f_skips = [900 6000 40 300 600]; %skip these bytes after each read instruction

data = []; %save the result here
fseek(fi,90000,'bof'); %skip initial bytes until first read

%read the file
for ind=1:nbr_read_skip_cylces-1
  tmp_data = fread(fi,f_reads(ind));
  data = [data; tmp_data]; %add newly read bytes to data variable 
  fseek(fi,f_skips(ind),'cof'); %skip to next read position
end

仅供参考:为了获得概览和透明性,我编制了一些图(下图),这些图是第一个 ca 6.500 读取位置(我的实际数据),在折叠成 fread-fseek 对后,可以总结为 1.200 fread- fseek 对。

我会做两件事来加速你的代码:

  1. preallocate 数据数组。
  2. 写一个CMEX-file调用freadfseek.

这是我使用来自 MATLAB 或 C:

freadfseek 进行比较的快速测试
%% Create large binary file
data = 1:10000000; % 80 MB
fi = fopen('data.bin', 'wb');
fwrite(fi, data, 'double');
fclose(fi);

n_read = 1;
n_skip = 99;

%% Read using MATLAB
tic
fi = fopen('data.bin', 'rb');
fseek(fi, 0, 'eof');
sz = ftell(fi);
sz = floor(sz / (n_read + n_skip));
data = zeros(1, sz);
fseek(fi, 0, 'bof');
for ind = 1:sz
  data(ind) = fread(fi, n_read, 'int8');
  fseek(fi, n_skip, 'cof');
end
toc

%% Read using C MEX-file
mex fread_test_mex.c

tic
data = fread_test_mex('data.bin', n_read, n_skip);
toc

这是fread_test_mex.c:

#include <stdio.h>
#include <mex.h>

void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
{
   // No testing of inputs...
   // inputs = 'data.bin', 1, 99
   char* fname = mxArrayToString(prhs[0]);
   int n_read = mxGetScalar(prhs[1]);
   int n_skip = mxGetScalar(prhs[2]);
   FILE* fi = fopen(fname, "rb");
   fseek(fi, 0L, SEEK_END);
   int sz = ftell(fi);
   sz /= n_read + n_skip;
   plhs[0] = mxCreateNumericMatrix(1, sz, mxDOUBLE_CLASS, mxREAL);
   double* data = mxGetPr(plhs[0]);
   fseek(fi, 0L, SEEK_SET);
   char buffer[1];
   for(int ind = 1; ind < sz; ++ind) {
      fread(buffer, 1, n_read, fi);
      data[ind] = buffer[0];
      fseek(fi, n_skip, SEEK_CUR);
   }
   fclose(fi);
}

我看到了这个:

Elapsed time is 6.785304 seconds.
Building with 'Xcode with Clang'.
MEX completed successfully.
Elapsed time is 1.376540 seconds.

也就是说,读取数据的速度是 C MEX-file 的 5 倍。那段时间包括将 MEX-file 加载到内存中。第二个 运行 快一点(1.14 秒),因为 MEX-file 已经加载。


在 MATLAB 代码中,如果我初始化 data = []; 然后每次读取时都像 OP 那样扩展矩阵:

tmp = fread(fi, n_read, 'int8');
data = [data, tmp];

那么该循环的执行时间为 159 秒,其中 92.0% 的时间花费在 data = [data, tmp] 行。 预分配真的很重要!