MATLAB fwrite with skip slow
MATLAB fwrite with skip slow
我正在使用 fwrite 命令在 MATLAB 中写入一些更大的 (~500MB - 3GB) 二进制数据。
我希望数据以表格格式写入,所以我使用了 skip 参数。例如。我有 2 个 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
我可以理解 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
我对该功能进行了一些非常有限的测试,但它似乎按预期工作。返回的
然后可以在不使用 skip 参数的情况下将缓冲区矩阵传递给 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
为获得最佳 I/O 性能,请使用顺序写入并避免跳过。
- 在保存到文件之前重新排序 RAM 中的数据。
重新排序 RAM 中的数据比重新排序磁盘上的数据快 100 倍。
I/O 操作和存储设备针对大数据块的顺序写入进行了优化(在硬件和软件方面都进行了优化)。
在机械硬盘(HDD)中,跳过写入数据可能需要很长时间,因为驱动器的机械磁头必须移动(通常OS通过使用内存缓冲区对其进行优化,但原则上这需要很长时间)。
使用 SSD,没有机械寻道,但顺序写入仍然快得多。阅读以下 post 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:
Elapsed time is 56.363397 seconds.
Elapsed time is 0.280049 seconds.
- 正在将文件写入硬盘:
Elapsed time is 56.063186 seconds.
Elapsed time is 0.522933 seconds.
- 正在 RAM 中重新排序
d
:
Elapsed time is 0.965358 seconds.
为什么慢 300 倍而不是 4 倍?
我猜想通过跳过写入数据的软件实现并未针对最佳性能进行优化。
根据以下post:
fseek()
or fflush()
require the library to commit buffered operations.
丹尼尔的猜测(在评论中)可能是正确的。
"The skip causes MATLAB to flush after each byte."
跳过可能是使用 fseek()
实现的,并且 fseek()
强制将数据刷新到磁盘。
它可以解释为什么跳过的写作速度非常慢。
我正在使用 fwrite 命令在 MATLAB 中写入一些更大的 (~500MB - 3GB) 二进制数据。
我希望数据以表格格式写入,所以我使用了 skip 参数。例如。我有 2 个 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
我可以理解 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
我对该功能进行了一些非常有限的测试,但它似乎按预期工作。返回的 然后可以在不使用 skip 参数的情况下将缓冲区矩阵传递给 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
为获得最佳 I/O 性能,请使用顺序写入并避免跳过。
- 在保存到文件之前重新排序 RAM 中的数据。
重新排序 RAM 中的数据比重新排序磁盘上的数据快 100 倍。
I/O 操作和存储设备针对大数据块的顺序写入进行了优化(在硬件和软件方面都进行了优化)。
在机械硬盘(HDD)中,跳过写入数据可能需要很长时间,因为驱动器的机械磁头必须移动(通常OS通过使用内存缓冲区对其进行优化,但原则上这需要很长时间)。
使用 SSD,没有机械寻道,但顺序写入仍然快得多。阅读以下 post 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:
Elapsed time is 56.363397 seconds.
Elapsed time is 0.280049 seconds.
- 正在将文件写入硬盘:
Elapsed time is 56.063186 seconds.
Elapsed time is 0.522933 seconds.
- 正在 RAM 中重新排序
d
:
Elapsed time is 0.965358 seconds.
为什么慢 300 倍而不是 4 倍?
我猜想通过跳过写入数据的软件实现并未针对最佳性能进行优化。
根据以下post:
fseek()
orfflush()
require the library to commit buffered operations.
丹尼尔的猜测(在评论中)可能是正确的。
"The skip causes MATLAB to flush after each byte."
跳过可能是使用 fseek()
实现的,并且 fseek()
强制将数据刷新到磁盘。
它可以解释为什么跳过的写作速度非常慢。