|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
8 I. t O' t1 P( l
今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。
. @( _; R9 Q! ~' t+ y. M9 L h
) F: K2 P) ?, C; I6 R& |Flushing 和 Buffer. }, a& Q, i& e3 @ j
~1 {3 [* R6 d( y
不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及
8 Z0 ?* m1 C p, L3 B7 }. P. ]' D3 c* R1 l! S8 x: }" K, w
, h% R4 |1 K, G9 q4 x" i7 p0 ^! n% T
看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
; H8 ^# f ~. H$ x% B
; d& C0 N, ~# r2 y3 a i& h p下面看一组比较数据,首先我们没有使用缓存方式6 N2 [: x8 @1 W3 O) W- s0 A5 N% ]
- 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.
* J, G# _# S) k: W
; s1 L4 P0 Z$ X3 X
/ n; L3 I# `- C% l消耗了大概15s,下面看看使用缓存的方式
# s) a1 D; f& U# e% @! l+ x8 }- k E# _7 n4 w- r$ g
- % 缓存输出模式 – 快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.
! F) i( _5 S6 r: x! B) i7 L/ i 9 Y4 ?; Q6 M. t0 ?4 f- Q
, G4 o6 b7 t! q4 ^8 u
使用缓存后时间缩短至5.6s,看来效率提高了不少呀!9 ^( ]# |: H w3 r
+ p |; L+ [ B2 W1 K
我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!4 L6 F" S- A0 P8 W/ m
+ O2 k1 K2 m r! ?2 t
Chunking I/O
6 m2 y* K' N! b3 f& Q! d2 j F& M7 X* W
使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
5 K3 S' `( g- O8 h. a6 i) s! F
/ n9 |6 u: s- j3 O) z9 A, ^- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.
7 Y" ~$ c8 v* D3 x
1 |, I X. Z8 I! v- U! O
( z5 F" O% D- {3 d可以看出即使是非缓存模式,写入30ms的效率还是很高的。
6 E1 [7 S! V& g8 ]5 A: u4 |1 \& u" Y: y, E
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。 W' |6 u0 C: {
5 q, N4 y* |7 H, Y5 {# o4 T$ \
- 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);
) q) Z+ ~$ c5 Z6 B! l
/ M8 |) E- s; x' [
8 _+ ]8 a( J, V7 L+ c" c, Q总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!
9 E; Q( {& R% e: a
) R- j1 H, @$ [+ S. k- g7 k0 [. u. B% z4 R
|
|