|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
0 @3 [5 J2 s& G& R5 B今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。
* [9 k. @2 ]- [6 S6 H! D/ D6 g0 p: G# g U/ m
Flushing 和 Buffer. i) m& m Y: m, U# R l! H$ E1 O
0 C" ^3 @/ W1 o4 o6 x6 e
不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及
* w: D% ?2 l2 M, i% N% | ?
: H- C- j5 r2 Q. G, M4 ?
& C0 X" l7 ^6 \( J+ S9 p
" z2 N; U$ Y/ a! j/ l9 u; `
看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!% x' z8 S7 p1 y, r
' I, m, k) e7 F5 [( S2 L下面看一组比较数据,首先我们没有使用缓存方式: [: F* q0 M; k6 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.: z* v+ _2 j8 a' o1 u% \
& r0 P7 q I2 k5 I3 H
( e) S* D6 |' }. K消耗了大概15s,下面看看使用缓存的方式
: l* V) T5 D% t+ _2 }" |
' Q4 N' m) Q6 p) B- % 缓存输出模式 – 快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., Y7 O9 v+ }5 S( x4 ^/ K. W8 M9 }
, |. e% Y. h4 H Q
/ r8 C; Q" O+ P% J" a+ F
使用缓存后时间缩短至5.6s,看来效率提高了不少呀!
- x" ^& _% q- {6 t
+ V0 N" f8 Z. g9 [+ A8 r6 i我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!
- v9 L1 Q. O9 Z, ~4 S" H; V0 S, t& C0 j0 \
Chunking I/O" ?: S- ~& ?2 u& f9 f$ Q
0 ^ M. L" g1 B% Y# z使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
, @4 ^; N2 W9 y9 I/ W8 w/ e$ l R8 V2 M
- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.' @. u. ^9 Y }5 v4 m
6 ~! I; L A* N. U5 {/ K0 F
0 u: ]' t S' @
可以看出即使是非缓存模式,写入30ms的效率还是很高的。
& U+ C8 Y6 A) H2 r( |' @+ n- W8 |' W6 x# W
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。# F7 F% N! [/ o! T+ G" J" f# Z
8 l5 m% T( s* J- 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);/ d% w# X$ N% q/ I
5 M8 U# E* C7 {* ?
, @3 J* `$ }$ |总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!
6 y) ~/ U% Z0 B }3 A9 d Z, Q7 m; @6 g6 R. e2 [
?/ Q$ C2 }* T4 E* Y( {/ F' P |
|