在Matlab中快速加载大型二进制文件的块

4

我有一些庞大的数据文件(256个通道,大约7500-1亿个样本=每个文件约40-50 GB),以int16格式编写。它是以平面二进制格式编写的,因此结构类似于:CH1S1,CH2S1,CH3S1 ... CH256S1,CH1S2,CH2S2,...

我需要单独读取每个通道,过滤并校正偏移,然后保存。我目前的瓶颈是加载每个通道,这需要大约7-8分钟...将其扩展256倍,我需要近30小时才能加载数据!我正在尝试聪明地使用fread,跳过每个通道读取时的字节;我在循环中使用以下代码来处理所有256个通道:

offset = i - 1;
fseek(fid,offset*2,'bof');
dat = fread(fid,[1,nSampsTotal],'*int16',(nChan-1)*2);

阅读相关资料,这通常是加载大型二进制文件部分内容的最快方法,但是该文件是否太大以至于无法更快地完成此操作?
我没有加载那么多数据...我正在使用的测试文件大小为37GB,对于256个通道中的一个通道,我只加载了整个跟踪的149MB...也许fread的“跳过”功能不够优化?
系统详细信息:MATLAB 2017a,Windows 7,64位,32GB RAM

2
如果你有能力一次性加载整个文件,那就这么做。否则,尝试加载大块内容并使用索引来保留所需部分,看看是否更快。 - Cris Luengo
1
Q1)为什么你需要“分别读取每个通道”? Q2)为什么不将数据存储在更合理的格式中,或使用高性能语言临时分割数据,在R中进行过滤,然后使用高性能语言重新组装它? - Mark Setchell
1
我使用的是带有NVME SSD驱动器的Mac,它可以维持2GB/s的速度,因此可以在25秒内读取50GB的文件,并在另外30-40秒内将其写入。传统的硬盘驱动器可能会慢上10-15倍左右,所以您需要在15分钟内拆分文件,然后在同样的时间内重新组合文件。不确定每个通道的处理需要多长时间,但如果每个通道的数据是单独的,那么您可以轻松地将其跨4-8个线程并行化 - 正如我所建议的那样。 - Mark Setchell
我建议您读取一块(比如10GB)数据而不跳过任何部分,然后使用MATLAB索引提取您的通道,接着读取下一块数据,以此类推。然后将这些块组合起来。比较这种方法的执行时间与您当前的方法,看看是否有差异。 - Cris Luengo
@CrisLuengo 听起来不错。我认为秘诀在于按顺序阅读而不是跳跃。如果你跳到每一个256个通道的100m样本,那就是大量的系统调用,而延迟是累计的。 - Mark Setchell
显示剩余3条评论
1个回答

5

@CrisLuengo的想法更快:基本上,将数据分块,加载每个块,然后将其拆分成单独的通道文件以节省RAM。

这里是仅加载部分的一些代码,速度很快,不到1分钟:

% fake raw data
disp('building... ');
nChan = 256;
nSampsTotal = 10e6;
tic; DATA = rand(nChan,nSampsTotal); toc;
fid = fopen('rawData.dat','w');
disp('writing flat binary file... ');
tic; fwrite(fid,DATA(:),'int16'); toc;
fclose(fid);

% compute the number of samples and chunks
chunkSize = 1e6;
nChunksTotal = ceil(nSampsTotal/chunkSize);


%% load by chunks
t1 = tic;
fid = fopen('rawData.dat','r');
dat = zeros(nChan,chunkSize,'int16');
chunkCnt = 1;
while 1
    tic
    if chunkCnt <= nChunksTotal
        % load the data
        fprintf('Chunk %02d/%02d: loading... ',chunkCnt,nChunksTotal);
        dat = fread(fid,[nChan,chunkSize],'*int16');
    else
        break;
    end
    toc;
    chunkCnt = chunkCnt + 1;
end
t = toc(t1); fprintf('Total time: %4.2f secs.\n\n\n',t);
% Total time: 55.07 secs.
fclose(fid);

另一方面,通过跳过文件进行通道加载需要的时间大约是原来的20倍,略长于20分钟:

%% load by channels (slow)
t1 = tic;
fid = fopen('rawData.dat','r');
dat = zeros(1,nSampsTotal);
for i = 1:nChan
    tic;
    fprintf('Channel %03d/%03d: loading... ');
    offset = i-1;
    fseek(fid,offset*2,'bof');
    dat = fread(fid,[1,nSampsTotal],'*int16',(nChan-1)*2);
    toc;
end
t = toc(t1); fprintf('Total time: %4.2f secs.\n\n\n',t);
% Total time: 1133.48 secs.
fclose(fid);

我还要感谢 Matlab 论坛上 OCDER 的帮助:链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接