MATLAB fwrite 带有跳过操作速度慢

3
我正在使用MATLAB中的fwrite命令编写一些较大(~500MB - 3GB)的二进制数据。我希望将数据以表格形式书写,因此使用了skip参数。例如,我有两个uint8值向量 a = [1 2 3 4]; b = [5 6 7 8]。我想让二进制文件看起来像这样 1 5 2 6 3 7 4 8
因此,在我的代码中,我会类似于以下操作(我的数据更加复杂):
fwrite(f,a,'1*uint8',1);
fseek(f,2)
fwrite(f,b,'1*uint8',1);

但写入速度非常慢( 2MB/s )。

我运行了下面的代码块,当我设置跳过计数为1时,写入速度大约慢了300倍。

>> f = fopen('testfile.bin', 'w');
>> d = uint8(1:500e6);
>> tic; fwrite(f,d,'1*uint8',1); toc
Elapsed time is 58.759686 seconds.
>> tic; fwrite(f,d,'1*uint8',0); toc
Elapsed time is 0.200684 seconds.
>> 58.759686/0.200684

ans =

  292.7971

我能理解skip参数设置为1后需要遍历两倍的字节大小会导致2倍或4倍的减速,但是300倍的减速让我觉得自己做错了什么。

有人遇到过这种情况吗?有没有方法可以加速写入操作?

谢谢!

更新

我编写了以下函数来格式化任意数据集。对于大数据集,写入速度得到了极大的提升(约300MB/s)。

%
%  data: A cell array of matrices. Matrices can be composed of any
%        non-complex numeric data. Each entry in data is considered
%        to be an independent column in the data file. Rows are indexed
%        by the last column in the numeric matrix hence the count of elements
%        in the last dimension of the matrix must match. 
%
%   e.g. 
%   size(data{1}) == [1,5]
%   size(data{2}) == [4,5]
%   size(data{3}) == [3,2,5]
%
%   The data variable has 3 columns and 5 rows. Column 1 is made of scalar values
%   Column 2 is made of vectors of length 4. And column 3 is made of 3 x 2 
%   matrices
%
% 
%  returns buffer: a N x M matrix of bytes where N is the number of bytes
%  of each row of data, and M is the number of rows of data. 

function [buffer] = makeTabularDataBuffer(data)
    dataTypes = {};
    dataTypesLengthBytes = [];
    rowElementCounts = []; %the number of elements in each "row"

    rowCount = [];

    %figure out properties of tabular data
    for idx = 1:length(data)

        cDat = data{idx};
        dimSize = size(cDat);

        %ensure each column has the same number of rows.
        if isempty(rowCount)
            rowCount = dimSize(end);
        else
            if dimSize(end) ~= rowCount
                throw(MException('e:e', sprintf('data column %d does not have the required number of rows (%d)\n',idx,rowCount)));
            end
        end

        dataTypes{idx} = class(data{idx});
        dataTypesLengthBytes(idx) = length(typecast(eval([dataTypes{idx},'(1)']),'uint8'));
        rowElementCounts(idx) = prod(dimSize(1:end-1));

    end

    rowLengthBytes = sum(rowElementCounts .* dataTypesLengthBytes);
    buffer = zeros(rowLengthBytes, rowCount,'uint8'); %rows of the dataset map to column in the buffer matrix because fwrite writes columnwise

    bufferRowStartIdxs = cumsum([1 dataTypesLengthBytes .* rowElementCounts]);

    %load data 1 column at a time into the buffer
    for idx = 1:length(data)
        cDat = data{idx};
        columnWidthBytes = dataTypesLengthBytes(idx)*rowElementCounts(idx);

        cRowIdxs = bufferRowStartIdxs(idx):(bufferRowStartIdxs(idx+1)-1);

        buffer(cRowIdxs,:) = reshape(typecast(cDat(:),'uint8'),columnWidthBytes,[]); 
    end

end

我对该函数进行了有限的测试,但看起来它已经按照预期工作了。返回的缓冲矩阵可以直接传递给fwrite函数,无需跳过参数,fwrite将以列主序写入缓冲区。

dat = {};
dat{1} = uint16([1 2 3 4]);
dat{2} = uint16([5 6 7 8]);
dat{3} = double([9 10 ; 11 12; 13 14; 15 16])';

buffer = makeTabularDataBuffer(dat)

buffer =

  20×4 uint8 matrix

    1    2    3    4
    0    0    0    0
    5    6    7    8
    0    0    0    0
    0    0    0    0
    0    0    0    0
    0    0    0    0
    0    0    0    0
    0    0    0    0
    0    0    0    0
   34   38   42   46
   64   64   64   64
    0    0    0    0
    0    0    0    0
    0    0    0    0
    0    0    0    0
    0    0    0    0
    0    0    0    0
   36   40   44   48
   64   64   64   64
1个回答

4
为了获得最佳的I/O性能,请使用顺序写入并避免跳跃。
  • 在保存到文件之前,重新排列RAM中的数据。
    在RAM中重新排列数据比在磁盘上重新排列数据快100倍。

I/O操作和存储设备都针对大数据块的顺序写入进行了优化(硬件和软件都经过优化)。

在机械驱动器(HDD)中,带有跳跃的数据写入可能需要很长时间,因为驱动器的机械头必须移动(通常使用内存缓冲区来优化,但原则上需要很长时间)。

对于SSD,没有机械寻道,但顺序写入仍然要快得多。阅读以下文章Sequential vs Random I/O on SSDs?以获取一些解释。


重新排序RAM中的数据示例:

a = uint8([1 2 3 4]);
b = uint8([5 6 7 8]);

% Allocate memory space for reordered elements (use uint8 type to save RAM).
c = zeros(1, length(a) + length(b), 'uint8');

%Reorder a and b in the RAM.
c(1:2:end) = a;
c(2:2:end) = b;

% Write array c to file
fwrite(f, c, 'uint8');
fclose(f);

我的机器的时间测量:

  • 将文件写入SSD:
    经过56.363397秒的时间。
    经过0.280049秒的时间。
  • 将文件写入HDD:
    经过56.063186秒的时间。
    经过0.522933秒的时间。
  • 在RAM中重新排序 d
    经过0.965358秒的时间。

为什么比4倍慢了300倍?
我猜测跳过写入数据的软件实现没有针对最佳性能进行优化。


根据以下post

fseek()fflush()需要库来提交缓冲操作。

丹尼尔的猜测(在评论中)可能是正确的。
“跳过导致MATLAB在每个字节后刷新。”
跳过可能使用fseek()实现,并且fseek()强制刷新数据到磁盘。
这可以解释为什么带有跳过的写入速度非常慢。

我猜是 skip 导致 MATLAB 每个字节后都进行刷新。 - Daniel

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