|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
8 J+ L% S: | L5 p3 `1 [( T/ d
今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。. t& U3 F- {3 [ P% p8 a
! m/ S8 A( j4 {# q3 `6 g2 l
Flushing 和 Buffer
& B) g0 q! p1 ?3 e% ]/ J: H* J& }. d8 I
不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及* {! f8 }( U. [! M0 X+ k/ M2 h
/ W; |+ \# n% `/ k/ f$ k
" b$ K6 q, v( O
5 m- {+ R" p) k* b) g8 d看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
( `& f. U- q2 }" Z0 d `
" K% C# a- M; y5 i0 Y" d下面看一组比较数据,首先我们没有使用缓存方式% ^# z: L/ u7 m5 x7 o( E! ]
- 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.
5 G9 v5 ^1 y/ S4 q, L, U2 R- e
- P: x; K J3 { X8 h" K" l" \4 B
& _ }0 y+ a* g/ l0 S4 g消耗了大概15s,下面看看使用缓存的方式+ V; a4 D& D+ C8 F8 C
: |) X0 `; s. d1 \" c9 F- % 缓存输出模式 – 快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.+ P; j2 ?8 b' B& R5 q" P" L: F
% D8 [; f7 R% f3 r: h; u
. l0 r. y" L1 G0 g8 V8 d" l3 ?+ }使用缓存后时间缩短至5.6s,看来效率提高了不少呀!
. |# c3 d% g& m( G& C- P
7 j/ K' @$ h8 g- p我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!
~; i, X# W* d' Q- J
/ E. k0 n* k2 x; Y9 }! r& S( gChunking I/O% r: P& r2 T; G- _
q E8 P( E6 A: }$ z c" v
使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
$ n" D( g/ t# d& b0 b" r7 Z2 X# t9 T/ [
- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.
' A+ Z6 _: W" I; T5 F0 V3 m 0 h: K6 g3 Z) p8 m$ Q X$ [7 ]
$ o- C; L N. `可以看出即使是非缓存模式,写入30ms的效率还是很高的。' M9 Y5 z1 W7 N3 z
! t, y# E# p" X! P$ g
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。7 p% t( q, ], E0 l! i( a
1 P: Z' K3 ~. l( b! o8 ]
- 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);. z; M3 P2 {6 D0 ~! _
: B) B4 ]. R& f' X- A# C
1 z9 Q6 n q: I; c
总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存! p/ J/ y/ }# x- O8 k: o3 W
8 j2 r9 W4 e* b2 c# c$ O. S' F5 x7 q( {% q0 }# e8 M
|
|