|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
# p8 o% a4 q }. H% l: e8 K
今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。- Y$ D2 Z* J1 l7 W. R
8 i) h- J( O" |( q |& KFlushing 和 Buffer
' k/ N8 I1 T' |# O4 o8 P. ?. L
7 O, n2 U8 @ L& g9 Q3 T不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及
" r$ ]) i$ Q `' M7 f, E& `6 n% J! L: k' G
: t& ]4 w# D) W+ e ?
- B! ^% g. ~) X+ Y! _
看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
# h3 F" Q1 o% e* o! k+ \+ Z7 j; M, f: @
下面看一组比较数据,首先我们没有使用缓存方式
% n |9 |+ T! V7 T4 f" w- data = randi(250,1e6,1); % 生成一组数据,用来测试I/O性能
- % 标准形式,没有应用缓存输出 - 慢
- fid = fopen('demo.dat', 'wb'); % w是小写,b表示二进制
- tic, for idx = 1:length(data), fwrite(fid,data(idx)); end, toc
- fclose(fid);
- Elapsed time is 14.983201 seconds.
- T8 \; @+ ?; J* I 4 T) A/ p! b6 v/ ~; k2 g t$ \
& Z- n1 V4 B( j' r1 P4 k4 {
消耗了大概15s,下面看看使用缓存的方式* P4 ~* z8 r5 K. ~4 y
4 y3 |+ X4 u& t$ y6 U- I- % 缓存输出模式 – 快3倍
- fid = fopen('demo.dat', 'Wb'); % 注意W是大写,b表示二进制
- tic, for idx = 1:length(data), fwrite(fid,data(idx)); end, toc
- fclose(fid);
- Elapsed time is 5.616357 seconds.$ a X! |9 h1 T$ g8 E3 d Z
4 v. c( K0 h: q
% [) Q4 _5 N8 u& Z使用缓存后时间缩短至5.6s,看来效率提高了不少呀!5 h0 V2 s/ c( Q$ ^
7 z, R( |! e& m( O( G! E
我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!
, x4 h! ^2 @0 @9 M# w" t+ v0 m0 j& Y; |$ P7 E
Chunking I/O! [3 ]$ F$ d6 j! d! G9 r- P
( I8 I y/ {" k7 W8 x使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
0 W& R$ ]# _ ~. b1 D, R$ e$ _: \+ B5 t% T
* s9 v; s7 {& \' X U; s: n- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.
- ^" r3 z2 O0 Q6 L4 [0 b 2 |& j6 [$ J2 x" T& ^
5 `7 Q. E, T `0 `! G! b7 p
可以看出即使是非缓存模式,写入30ms的效率还是很高的。
) ?& f5 f" Q( x/ C
X# A! y8 @% k3 e0 [5 Z" i但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。
! u" y- x" l ]* M: [5 f% ]* }( U; f& x( u- {
- h = waitbar(0, 'Saving data...', 'Name','Saving data...');
- cN = 100; % number of steps/chunks
- % Divide the data into chunks (last chunk is smaller than the rest)
- dN = length(data);
- dataIdx = [1 : round(dN/cN) : dN, dN+1]; % cN+1 chunk location indexes
- % Save the data
- fid = fopen('test.dat', 'Wb');
- for chunkIdx = 0 : cN-1
- % Update the progress bar
- fraction = chunkIdx/cN;
- msg = sprintf('Saving data... (%d%% done)', round(100*fraction));
- waitbar(fraction, h, msg);
- % Save the next data chunk
- chunkData = data(dataIdx(chunkIdx+1) : dataIdx(chunkIdx+2)-1);
- fwrite(fid,chunkData);
- end
- fclose(fid);
- close(h);5 Y) b* u D- ], }
, D) T4 y, Z# ~) ?) |, J" y+ Z8 z7 Q: Y3 u: c
总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!% M& v' Z" L( j* t% n
3 Z ~7 u% q) X5 U6 R8 p7 O
( g, ]. W0 t# _! v4 Z: x8 M, Z
|
|