4

我有多个大小约为 2GB 的文本文件(大约 7000 万行)。我还有一台四核机器,可以访问并行计算工具箱。

通常,您可能会打开文件并读取行:

f = fopen('file.txt');
l = fgets(f);
while ~ isempty(l)
    % do something with l
    l = fgets(f);
end

我想在我的 4 个核心上分配“做某事l”,但这当然需要使用parfor循环。这将要求我先将 2GB 文件(借用 Perl 术语)“啜”到 MATLAB 中,而不是动态处理。我实际上并不需要l,只是处理的结果。

有没有办法通过并行计算从文本文件中读取行?

编辑:值得一提的是,我可以提前找到确切的行数(!wc -l mygiantfile.txt)。

EDIT2:文件结构如下:

15 1180 62444 e0e0 049c f3ec 104

所以 3 个十进制数字、3 个十六进制数字和 1 个十进制数字。对 7000 万行重复此操作。

4

2 回答 2

2

一些 matlab 的内置函数支持多线程 - 列表在这里。不需要并行计算工具箱。

如果“用 l 做某事”可以从工具箱中受益,只需在阅读另一行之前实现该功能。

您可能还想使用读取整个文件

fid = fopen('textfile.txt');
C  = textscan(fid,'%s','delimiter','\n');
fclose(fid);

然后并行计算 C 中的单元格。


parfor如果读取时间是一个关键问题,您可能还希望在循环中访问部分数据文件。这是Edric M Ellis的一个例子。

%Some data
x = rand(1000, 10);
fh = fopen( 'tmp.bin', 'wb' );
fwrite( fh, x, 'double' );
fclose( fh );

% Read the data
y = zeros(1000, 10);
parfor ii = 1:10
    fh = fopen( 'tmp.bin', 'rb' );
    % Get to the correct spot in the file:
    offset_bytes = (ii-1) * 1000 * 8; % 8 bytes/double
    fseek( fh, offset_bytes, 'bof' );
    % read a column
    y(:,ii) = fread( fh, 1000, 'double' );
    fclose( fh );
end

% Check
assert( isequal( x, y ) );
于 2013-09-04T17:25:45.603 回答
2

根据要求,我展示了一个使用类的内存映射文件示例。memmapfile

由于您没有提供数据文件的确切格式,我将创建自己的。我正在创建的数据是一个行表N,每行包含 4 列:

  • 首先是一个double标量值
  • 第二个是一个single
  • 第三个是一个固定长度的字符串uint32,以十六进制表示法表示 a(例如D091BB44:)
  • 第四列是一个uint8

生成随机数据的代码,并将其写入上述结构的二进制文件:

% random data
N = 10;
data = [...
    num2cell(rand(N,1)), ...
    num2cell(rand(N,1,'single')), ...
    cellstr(dec2hex(randi(intmax('uint32'), [N,1]),8)), ...
    num2cell(randi([0 255], [N,1], 'uint8')) ...
];

% write to binary file
fid = fopen('file.bin', 'wb');
for i=1:N
    fwrite(fid, data{i,1}, 'double');
    fwrite(fid, data{i,2}, 'single');
    fwrite(fid, data{i,3}, 'char');
    fwrite(fid, data{i,4}, 'uint8');
end
fclose(fid);

这是在 HEX 编辑器中查看的结果文件:

在十六进制编辑器中查看的二进制文件

我们可以确认第一条记录(注意我的系统使用的是小端字节序):

>> num2hex(data{1,1})
ans =
3fd4d780d56f2ca6

>> num2hex(data{1,2})
ans =
3ddd473e

>> arrayfun(@dec2hex, double(data{1,3}), 'UniformOutput',false)
ans = 
    '46'    '35'    '36'    '32'    '37'    '35'    '32'    '46'

>> dec2hex(data{1,4})
ans =
C0

接下来我们使用内存映射打开文件:

m = memmapfile('file.bin', 'Offset',0, 'Repeat',Inf, 'Writable',false, ...
    'Format',{
        'double', [1 1], 'd';
        'single', [1 1], 's';
        'uint8' , [1 8], 'h';      % since it doesnt directly support char
        'uint8' , [1 1], 'i'});

现在我们可以将记录作为普通结构数组访问:

>> rec = m.Data;      % 10x1 struct array

>> rec(1)             % same as: data(1,:)
ans = 
    d: 0.3257
    s: 0.1080
    h: [70 53 54 50 55 53 50 70]
    i: 192

>> rec(4).d           % same as: data{4,1}
ans =
    0.5799

>> char(rec(10).h)    % same as: data{10,3}
ans =
2B2F493F

好处是,对于大型数据文件,您可以将映射“查看窗口”限制为记录的一小部分,并沿文件移动此视图:

% read the records two at-a-time
numRec = 10;                       % total number of records
lenRec = 8*1 + 4*1 + 1*8 + 1*1;    % length of each record in bytes
numRecPerView = 2;                 % how many records in a viewing window

m.Repeat = numRecPerView;
for i=1:(numRec/numRecPerView)
    % move the window along the file
    m.Offset = (i-1) * numRecPerView*lenRec;

    % read the two records in this window:
    %for j=1:numRecPerView, m.Data(j), end
    m.Data(1)
    m.Data(2)
end

使用内存映射访问文件的一部分

于 2013-09-05T16:04:05.293 回答