我正在使用fwrite命令在MATLAB中编写一些较大的(〜500MB-3GB)二进制数据。
我希望数据以表格格式写入,因此我使用了skip参数。例如。我有2个uint8值a = [ 1 2 3 4]; b = [5 6 7 8]
的 vector 。我希望二进制文件看起来像这个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
我可以理解2倍或4倍的速度下降,因为您必须在skip参数设置为1的情况下遍历两倍的字节,但是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而不使用skip参数,并且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
最佳答案
为了获得最佳的I / O性能,请使用顺序写入,并避免跳过。
对RAM中的数据进行重新排序的速度比对磁盘上的数据进行排序的速度快100倍。
I / O操作和存储设备针对大型数据块的顺序写入进行了优化(在硬件和软件方面均进行了优化)。
在机械驱动器(HDD)中,由于驱动器的机械磁头必须移动(通常OS通过使用内存缓冲区对其进行优化,但原则上会花费很长时间),因此通过跳过写入数据可能会花费很长时间。
使用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);
我的机器中的时间测量:
Elapsed time is 56.363397 seconds.
Elapsed time is 0.280049 seconds.
Elapsed time is 56.063186 seconds.
Elapsed time is 0.522933 seconds.
d
:Elapsed time is 0.965358 seconds.
为什么慢300倍而不是4倍?
我猜测使用跳过写入数据的软件实现并未针对最佳性能进行优化。
根据以下post:
fseek()
或fflush()
要求库提交缓冲的操作。丹尼尔(在评论中)的猜测可能是正确的。
“跳过会导致MATLAB在每个字节之后刷新。”
跳过可能是使用
fseek()
实现的,并且fseek()
强制将数据刷新到磁盘。它可以解释为什么用跳过写会很痛苦。