|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
( z& q- P' W/ s
今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。
5 Y7 v0 J7 N* ?9 x2 A# R! t6 r; P. b9 {5 t
Flushing 和 Buffer
0 L6 ^, k+ m9 _/ C$ y! }: j" b9 e/ T. n& {
不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及
% {& \+ H% H$ C
2 |! P* c$ h& Q" E) X& K: z
7 r/ ]& e( w, I+ f& j8 G! f
5 T9 H" q# Z( l6 Q+ x; A" ?+ z5 m2 F看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
# T+ a; F a% q1 }) q2 P0 |5 ~) ]' F% J( e8 n. p4 H
下面看一组比较数据,首先我们没有使用缓存方式9 w6 i( ]/ e' O, s5 S
- 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. E* y0 G: b6 u9 W
' C, ]# p1 }% R% ^8 z _& z% v
# H2 c7 d; w( N! I7 p; |0 d" f消耗了大概15s,下面看看使用缓存的方式/ U. t2 B& R, D. w3 x; K
& F: N3 s1 K6 F+ s$ ]
- % 缓存输出模式 – 快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.8 ^8 A+ ^( r# a9 D2 K4 A
4 ~3 a' L+ p; ~: G, x
' k p2 q0 m f% |2 _* l0 |) x6 m使用缓存后时间缩短至5.6s,看来效率提高了不少呀!1 L }9 M1 `/ p5 D* d/ v
c6 l0 ~ I( b4 j/ W1 ^
我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!
1 R. L, e' w9 R; M m
* P, P O+ U3 c. k5 K/ m L, K, ?+ s4 gChunking I/O
: A+ b, _, A9 |1 t7 d, k. ]1 r3 G5 I' m, x! \
使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
% `' [! p5 Q2 R' e6 J, m1 W9 x7 A4 U# `1 i6 k" \
- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.7 m9 ]. v& D4 S
7 U: \$ Y7 q! V2 }; [) _/ _4 l% |, i; y9 h, \9 g
可以看出即使是非缓存模式,写入30ms的效率还是很高的。
4 s: }& O( M( D) X- H$ y1 B3 |, }* ^* U8 v) X
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。
) X% D# g2 O0 S/ [0 z
) G( s2 _; p/ l& v$ T9 v- 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);
3 W+ G% C) H1 A% ]; I
8 T, N. P1 R$ V6 S0 z" @% L& D7 L9 L- E& f
总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!
4 a2 b2 O6 i# q% |2 [' f; |
7 Z# z4 a- u- }6 G
* W, w- k* i$ @0 l2 u* G |
|