|  | 
 
| 
: z5 q- a- Q' ^1 i! f
x
EDA365欢迎您登录!您需要 登录 才可以下载或查看,没有帐号?注册  今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。
 6 w0 @+ }2 X# D# C$ p* [
 3 M9 _8 M: {, EFlushing 和 Buffer
 8 S7 O3 a4 c0 \. n/ @7 q; ~+ d1 t; k% `) g2 f- P, z! V: H
 不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及! n5 ]! ^% {7 o! B  t& |! @
 ' _1 a0 ^5 r; u' W! p8 x3 q2 E" q. L& o
 
   6 }( X( o3 n) Y* @; j$ u8 E4 {6 w5 L
 看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
 $ A4 R9 P5 F% S& [0 U& l8 G6 N. Q1 P
 4 f) r+ y; g& k: W下面看一组比较数据,首先我们没有使用缓存方式
 $ ^. t( s8 p! a, h5 G
 : z/ z6 C* A0 F7 q7 f! s& Kdata = randi(250,1e6,1);  % 生成一组数据,用来测试I/O性能% 标准形式,没有应用缓存输出 - 慢fid = fopen('demo.dat', 'wb'); % w是小写,b表示二进制tic, for idx = 1:length(data), fwrite(fid,data(idx)); end, tocfclose(fid);Elapsed time is 14.983201 seconds.: ?( a5 Q* A5 B, O1 h9 f5 {
 0 K' l& q/ [& ]4 g& q& g2 h
 消耗了大概15s,下面看看使用缓存的方式, [4 t  L7 _' V" U; A) F. l  B
 
 + m$ W" n/ |2 P) u. m/ [
 4 P/ ]* J4 Z6 I4 P# T+ h: X% 缓存输出模式 – 快3倍fid = fopen('demo.dat', 'Wb'); % 注意W是大写,b表示二进制tic, for idx = 1:length(data), fwrite(fid,data(idx)); end, tocfclose(fid);Elapsed time is 5.616357 seconds.0 q( j# S  x, u# N: ^, Q5 K
 & _2 Q/ f3 M" J  h
 使用缓存后时间缩短至5.6s,看来效率提高了不少呀!$ @4 A1 T) ?9 z2 C
 
 - u7 s+ f- [) l) k& ]我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!
 ) U$ d- Q0 Y! q0 |; Y& j) s; y, z$ h6 y: u! N
 Chunking I/O
 # x6 c+ B7 R  |! w" s) W& e( G$ w2 C
 使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
 # \* H6 M* q( x+ J  t! \
 ; ]+ K* V  E% P) T
 1 [5 H- W- D' I# M8 ?, ^3 F! hfid = fopen('demo.dat', 'wb'); % 注意是小写wtic, fwrite(fid,data); tocfclose(fid);Elapsed time is 0.034816 seconds.: R6 A. h( @0 v1 d8 u$ H
 
 / P9 }4 {1 o: Y可以看出即使是非缓存模式,写入30ms的效率还是很高的。" A4 H# D4 r+ I
 
 " Q' N2 r$ e$ V0 X5 B8 p" ~5 p7 `但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。
 9 I1 H  W/ w' W) b# Y8 H
 ; _* S! ~; r' [4 X
 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 datafid = 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);endfclose(fid);close(h);. i0 D  O2 R: [3 u/ P4 R
 ; V+ U" ~( a5 r4 d% }% v9 d0 a- h3 O7 v: p4 O$ S5 E& Q4 w& K5 |' q- r
 总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!8 ~. d5 Y" j! V, Z  z, V, k+ h
 4 [$ ^$ L7 v4 j8 x8 [
 
 4 B% r4 e: j$ A$ u) V" ?
 | 
 |