|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
9 F y4 ]: k( [1 b
今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。: y! ~# F% t6 @$ o8 r+ ~0 t
: h0 a* _% N7 O% m# M+ g
Flushing 和 Buffer
/ f% A* x$ w' Q
$ Q/ R- X# O; I# U" x6 X不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及 y+ r( N% |, v* S" q7 V2 h$ Y1 x
% P) T# e: h; C; }/ C
u) |* u9 U+ b6 h
7 A( U1 ~. O' G! n- @2 x
看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
; M; \1 C0 r! R
- r( u/ H* H# {+ \下面看一组比较数据,首先我们没有使用缓存方式
' q" ^# U" X2 M( L: q& i& V# C- 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.# G- L! \2 z) K2 w i" d
* y3 C% D" \* n5 a+ u3 g% d
. `5 T6 j4 M( x2 W5 d( c/ p7 O; p( ]5 M消耗了大概15s,下面看看使用缓存的方式
0 _8 o2 Z! T( @* A9 q& @' r h
3 f' k, _; M* S, \) f/ W4 z- % 缓存输出模式 – 快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.
! O- r# |% U3 C" s) E
2 K1 G2 i8 ^# P k1 \
2 p$ I: C3 j* T/ H使用缓存后时间缩短至5.6s,看来效率提高了不少呀!
+ G2 ~, B2 C {5 w) I f" D
. S0 @' x. ?; E: x# e. L/ Y我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!
# G5 \: r* u7 S2 L" F1 s3 v# w
) c: g- y L/ _" g) x9 L6 {3 Y) WChunking I/O. T: f7 P2 C% a1 W
0 d0 j( ]9 | }
使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中5 j/ S; o; a+ |' U. R
2 v1 `8 ^( ~ c% H6 e! i1 [- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.4 E7 ^2 x: J2 u ~! C" w6 L j
. m. h i6 B, g3 Q- ]2 e1 U2 a6 k% C7 G5 }
可以看出即使是非缓存模式,写入30ms的效率还是很高的。
8 i4 E& a5 C1 _7 Y! B; g. b1 [' }, t5 e$ X5 g) s
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。3 [- c6 U4 \" h K3 U
" C3 S* Q* @" ~- 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);- k1 ?0 j: W& I, x
' ]% G& i$ m7 w, i/ p
* J* {9 x) I3 o/ z2 c, [/ W总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!
5 G$ h# ]9 V$ O6 s1 S! C* |6 F( e( Z2 {+ ~8 z, P, b0 Y' H
) r; C# m4 S& f2 z9 b
|
|