|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
( x+ B; P5 z# m8 V
今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。
& l& @. O9 U9 ~
. g$ p) g" m0 V! x# [; q& PFlushing 和 Buffer% I& I# G# ~- q
- r Z* q: g& e1 i0 d& u0 W
不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及3 y: c" W3 Q; H! v3 t
1 S. t& g: b |/ M; y
. q& V% J8 W8 C" ?/ H% J- m* Z& X4 ^
看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能!8 \! y/ p6 H j$ M/ @
* S1 Q; Z9 J/ N+ J9 l: u下面看一组比较数据,首先我们没有使用缓存方式$ B' M1 ^; J+ {5 g2 |0 b8 A
- 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.* X7 n$ d& Y0 K" j
$ n9 h; u$ Q# ~
* \6 g" H/ p! b' ~# u消耗了大概15s,下面看看使用缓存的方式
& y; z6 s3 n5 W+ I" N3 m$ S3 b* d! o% L; Q
- % 缓存输出模式 – 快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.1 V6 E1 Q- j J3 ?) \/ k4 e6 w9 B
& p7 S+ K7 m2 W! b, @) W
8 ~! P9 P3 F: x& b* e使用缓存后时间缩短至5.6s,看来效率提高了不少呀!' \- o, [8 h8 \; K% g5 H8 R& H, k( ]
+ V4 a% M7 O: N' @+ m我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧!! D- Q7 H9 I# \. |+ ^9 H: C2 I; r
& b. x' I9 _. g/ a/ L" z$ T: \4 iChunking I/O$ r, j6 ]% U) p; r2 z! S
$ c7 q- x: \4 v: v" c0 {( a使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
- C9 I, D0 l6 M8 i7 u, r7 J: I; e3 e" q- j
- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.
9 h3 t' b! n1 c- q$ x
0 d) Z& ~6 H( |0 B& ]' S" A7 h$ b* [$ T- }* F: `+ }
可以看出即使是非缓存模式,写入30ms的效率还是很高的。- o; N( B' n4 y# b: \, W4 E5 i
$ `1 k$ k% L4 K9 j! q
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。" O% p: O+ R* y: [/ X, O6 w5 H. j
5 @* a% G4 {% M- p+ K1 b/ r
- 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);
+ t$ W2 q% e' d0 K4 ?+ t
# O, q$ N4 I& c' j; a/ o
+ [& a# r7 w/ J! Y+ P+ {, u" A总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!
I/ q4 Q( f# W$ D6 `# t, e+ P3 I9 h3 d0 y& f, f2 ~
& [% s: n9 g: s: |8 m |
|