|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
% |: g2 B$ p; R* a# e5 ~5 @9 O$ n; d
今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。
* S ]: O9 s; v. p" k: Y
5 ]2 l( l( I _5 mFlushing 和 Buffer
; L1 z, T, K3 ?0 q
* N; s) \# e! J/ F0 @9 |不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及; l. L; o/ g1 W% C2 h z, J/ o6 P
* k1 ^/ f. _, H* ~5 Z; w7 `. t
$ D0 g8 ]! E0 J( z: e& W1 L
: P2 m" \. G8 T看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
( [0 n6 W) e2 d
+ w- f. u" C/ G; T下面看一组比较数据,首先我们没有使用缓存方式
# E: d9 ^, n; M6 q. d+ O* u- 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.
9 L! v7 w# D+ z$ b / h2 r: K$ D" Y+ `, V; O, o8 C8 M. E
; I/ {- Y% N( v# q0 x8 d. {2 b消耗了大概15s,下面看看使用缓存的方式
7 m* ]" I. v5 u1 c
! c- ]1 @. ^' L/ E7 }. W- % 缓存输出模式 – 快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 m+ Y8 J0 s2 U2 n5 ~) n0 T# Y
0 d5 Z; ?# ]3 W+ ~0 f- x$ K
/ W3 m N# z' t( z# M使用缓存后时间缩短至5.6s,看来效率提高了不少呀!5 ]1 F2 E8 E. n# C/ @4 Y
4 G; E8 c7 b/ P
我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!
2 z! U3 Y+ K* P( ?8 B( J3 k
7 W4 E, y# }+ Z& OChunking I/O
/ c4 J, F+ i, h7 c6 S3 W
9 E, t7 M. L, g& o0 }. _使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
, C$ J# @$ S1 d! C- V- y
0 c1 P8 M/ O) s' Q% T- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.1 f& V" G- ` E3 N8 ~1 u3 M
/ Z$ X* U8 A" d
. N) v3 ~- c7 J9 |# T) w可以看出即使是非缓存模式,写入30ms的效率还是很高的。, W2 w7 a3 G% B2 J
; j) J$ @- Z* H% b3 l! g$ V, M但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。: |, I5 e- F' r! U% A$ t
: F# s& h5 I3 v; ]% o
- 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);
p, L) z) N7 u, A2 S) A3 S
9 [( c8 L- I# _# I; l. u" x! g8 _
, i' Z& F! f1 o. k2 K总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!8 Y& F: u0 H2 o5 {# _- {
- j4 t+ S8 [3 c: M# R* n) ]* [; K! o- t# Z
|
|