|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
3 R6 I) ]7 a# d, Y' |% [ U今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。* O; b1 W+ Z) h' A( u& _) U
; S2 w# I5 R# t& [( J1 p% `/ v3 o$ o
Flushing 和 Buffer/ o8 S4 r, u8 G. g; p& a
G; k5 ~8 W9 p- R9 ?不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及7 [4 o2 f/ S+ W4 E
$ b Y2 A6 w0 ~
7 b0 F$ x& C6 J' f- X+ Q1 b
( x* m/ O, l# p4 i7 C9 }. c看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!
7 ?3 p" k2 @# X2 ^4 ^ m1 H$ N5 p8 d1 y1 Q# B+ r
下面看一组比较数据,首先我们没有使用缓存方式
4 p6 E) j8 ?( E, R3 J7 x1 o, r- 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.3 @9 d! d6 j% ]+ I! I
+ @" `. ?, ^! p4 Z+ d
+ b; Z/ Q, t3 c' A8 i消耗了大概15s,下面看看使用缓存的方式
/ K0 R/ Z2 E& R' m7 p( z7 H2 c) Z. h4 ^- r2 [0 v2 E
- % 缓存输出模式 – 快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.
/ e u8 ^- q( g4 b, T
2 F# R4 s" t: S
$ P3 @7 {4 X" u9 M使用缓存后时间缩短至5.6s,看来效率提高了不少呀!
$ \! F9 V0 z2 J8 o6 B& T: D) f# H* s% ~
我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!) L$ z4 b; _& F
# u f" p% t3 w1 d7 aChunking I/O" i! r* H( K0 E6 }: r) l
( j& h8 F) B0 D" R2 R* \! G使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
. M# h! D8 ^3 d" h( I( l& K& H; ] }1 ~
- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.
: {" G" X* e9 {5 o2 m- n) v1 [
( N1 \9 d J, }5 m/ W# U6 M) [5 t/ G1 Q
可以看出即使是非缓存模式,写入30ms的效率还是很高的。2 F& K" C. i/ e. h$ G
v2 p* M7 B/ _" B* U3 i9 `) w
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。
" V( s. K* ^1 ^2 ~! _" n9 U& |
3 _9 U+ ^- D9 H! A- 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);$ O* V3 R$ `' B, b
6 x" ~# B- _3 h# _4 G$ i8 S. f0 I) M
总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!
1 H% I0 J1 `; m/ [0 B$ D |8 N- G: |; x- d3 R
M( m' P4 v* @ |
|