Matlab:读取大型二进制文件 parts/sequences 的最快方法
Matlab: fastest method of reading parts/sequences of a large binary file
我想从一个大的(大约 11 GB)二进制文件中读取部分内容。当前有效的解决方案是用 fread()
加载整个文件 ( raw_data
),然后裁剪出感兴趣的部分 ( data
)。
问题: 是否有更快的方法读取文件的小部分(占总文件的 1-2%,部分顺序读取),给定二进制掩码(即 Matlab 中感兴趣的特定字节的逻辑索引)?具体如下。
我的具体案例说明:
感兴趣的 data
(26+e6 字节,或约 24 MB)大约是 raw_data
(1.2e+10 字节或约 11 GB)的 2%
- 每 600.000 个字节包含大约 6.500 个字节的读取,这可以分解为大约 1.200 个读取-跳过周期(例如“读取 10 个字节,跳过 5000 个字节”)。
- 整个文件的读取指令可以分解为大约 20.000 个相似但(不完全相同)的读取跳过周期(即大约 20.000x1.200 个读取跳过周期)
- 文件是从 GPFS(并行文件系统)读取的
- 过多的 RAM,最新的 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 对。
我会做两件事来加速你的代码:
- preallocate 数据数组。
- 写一个CMEX-file调用
fread
和fseek
.
这是我使用来自 MATLAB 或 C:
的 fread
和 fseek
进行比较的快速测试
%% 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]
行。 预分配真的很重要!
我想从一个大的(大约 11 GB)二进制文件中读取部分内容。当前有效的解决方案是用 fread()
加载整个文件 ( raw_data
),然后裁剪出感兴趣的部分 ( data
)。
问题: 是否有更快的方法读取文件的小部分(占总文件的 1-2%,部分顺序读取),给定二进制掩码(即 Matlab 中感兴趣的特定字节的逻辑索引)?具体如下。
我的具体案例说明:
-
感兴趣的
data
(26+e6 字节,或约 24 MB)大约是raw_data
(1.2e+10 字节或约 11 GB)的 2%- 每 600.000 个字节包含大约 6.500 个字节的读取,这可以分解为大约 1.200 个读取-跳过周期(例如“读取 10 个字节,跳过 5000 个字节”)。
- 整个文件的读取指令可以分解为大约 20.000 个相似但(不完全相同)的读取跳过周期(即大约 20.000x1.200 个读取跳过周期)
- 文件是从 GPFS(并行文件系统)读取的
- 过多的 RAM,最新的 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 对。
我会做两件事来加速你的代码:
- preallocate 数据数组。
- 写一个CMEX-file调用
fread
和fseek
.
这是我使用来自 MATLAB 或 C:
的fread
和 fseek
进行比较的快速测试
%% 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]
行。 预分配真的很重要!