|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
0 m& K' _0 w J+ [3 n' N( L& L今天我们将讨论下著名的fwrite(fprintf)函数,它们是用来进行二进制(文本)文件写入操作的。由于fwrite函数是底层I/O函数,且使用十分频繁,很多用户会质疑,它怎么可能还有性能提升的空间,要是有MathWorks早就更新了。# R9 {! `1 S$ L3 v- F& E
, Z0 F" S, r w1 x5 S4 l) V' GFlushing 和 Buffer
0 q" g0 g$ Y7 y/ [3 V7 p) u
% j) E: ?9 P3 Y! C不像C/C++语言,在MATLAB中调用fwirte(fprintf)函数时,MATLAB会自动刷新(flush)输出缓存(buffer),这一点MATLAB的帮助文档没有正面直接了当的说明,只是在fopen函数中隐晦的涉及: s- {# q$ B3 j' e
6 o6 J2 X$ G3 w' N6 j: U8 Q
' H" m: i9 p# @5 x9 W/ U
: v/ i8 k4 H }. @& l2 }0 q1 @看到这里,很多用户估计应该猜到了该怎么做了。在写入数据的时候,假如没有缓存(buffer),那么每次调用fwrite函数,就需要进行一次文件写入操作,这种方式将严重降低I/O性能! ~: t5 D! j' U1 `
0 g3 Z- z2 y2 A( W+ ?6 s L下面看一组比较数据,首先我们没有使用缓存方式
: F9 ~( E7 y. ]7 _2 G: m3 ~* g' G- 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.
# N' E1 e. c+ Q- x4 C . H+ F- j# q2 P* U
5 K* ]" B. {" |' l消耗了大概15s,下面看看使用缓存的方式
% r! ~8 L \ {5 T" X7 O |
& ?! f" U7 K" h6 [- % 缓存输出模式 – 快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.
! I8 i" ?% ~& l, R ) [9 R& h& `" g3 z
# z8 b" ^& r- B+ `" @1 g- k使用缓存后时间缩短至5.6s,看来效率提高了不少呀!
0 ]# S2 I6 ?8 T* B1 ~6 I. @% d& s7 w( D5 o' Z
我们无法理解MathWorks为什么将fopen默认设置为非缓存模式,但也许有他们的理由吧!不过当您在写大型数据文件的时候,推荐还是使用缓存模式('w')吧! i/ _4 L* _& |# a4 y, r; K
) b" N* ~3 p" s4 Y8 a fChunking I/O, U/ r6 c" ~8 A/ Q
$ ]! E& ~8 F6 q6 X使用缓存进行写操作,其实就是为了减少数据文件的访问次数,因此在MATLAB中,假如先将所有的数据都准备好,然后一次性调用fwrite函数将其写入文件中
( w2 T6 V: _3 I h+ Q7 O2 F# Y& A, ]# P( z/ i$ L( Y6 F2 {
- fid = fopen('demo.dat', 'wb'); % 注意是小写w
- tic, fwrite(fid,data); toc
- fclose(fid);
- Elapsed time is 0.034816 seconds.
+ h8 D" p4 |* V; F0 k% z/ v5 C6 W
' N4 G" k3 }, U5 O6 G- C R
! i/ i9 r4 J7 k1 Q% K可以看出即使是非缓存模式,写入30ms的效率还是很高的。
6 W3 f$ J7 K# ^+ j0 r- b; C( w. Z4 H! d6 q
但是假如我们读写的是网络文件,由于网络原因可能需要较长的时间,此时将大数据拆开成很多小块,然后分块处理是一个明智的选择。
! x; X6 P1 g/ v3 t7 x- u" E* j u7 {- 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);: x: ? s/ G8 r* `0 U6 Y
! Y+ Q8 L2 ]3 L0 Z
) y, S" I, q* C! h# u( Y& R总的来说,本文中的技术同时适用于fprintf和fwrite函数,但是存储和读取二进制文件(fwrite/fread)远远快于文本文件(fprintf/fscanf/textscan),因此如果数据不是为了人为可读,尽量使用二进制保存!
' G" Q1 {: ~( z" Z" ~1 w. E) N' Z# M" l0 a4 I6 X2 T; m# M9 B
! g+ n7 [& z6 f J |
|