ITEM: AN7235L

How does fwrite() and fflush() work?


Env:
.AIX 3.2.5
.Desc:
.Question from customer:
.I have an application where I am seeing some strange performance.  
The transfer rate for the first pass of the application was 3.8 
Mb/sec, while the transfer rate for the second pass was 0.6 
Mb/sec.  The 'monitor' command shows that there is no read 
activity on the disk during the first write pass.  This makes 
sense, because the file is new, so there is no need to save any 
disk block which may be partially rewritten.  On the second pass, 
however, 'monitor' shows an equal amount of read and write 
activity on the disk.  I understand why the first and last disk
block of the record to be written must be read first, since the 
starting and ending offsets will usually not be on a disk block 
boundary, but I don't see why the intermediate disk blocks are 
read since they will be completely rewritten.  We don't see this 
kind of dropoff in the transfer rate when our application runs on 
the SGI.
.I've discovered that the problem disappears if the fflush() call 
preceding the write is commented out.  The fflush() call is in our 
I/O routine before each read or write, presumably to make sure 
that buffered data is written to the disk before switching from 
write mode to read mode.  I think we could add logic to do the 
flush only when switching from write to read, but I'd like you to 
see if you can find out precisely when a flush is required, since 
this is not clear to me from the man pages.  Is it possible that 
the system level I/O routines are smart enough to issue a flush 
when required anyway, and that we don't need to do it at all?  I'm
wondering if that's what happens on the SGI, i. e., maybe our flush 
calls are treated as no-ops, and the system I/O routines take care 
of it automatically?
.Response:
.fwrite() will flush the buffer ONLY if the buffer is full or if the
input buffer contains a newline.  If neither of these conditions
exist, the buffer will not be flushed by fwrite().
.The fflush() subroutine uses write() to write any buffered data for
the stream and marks the st_ctime and st_mtime fields of the
underlying file for update.  fflush() also updates whatever internal
structures are used by the buffered I/O routines (like fwrite, fgets,
fread, i.e. things that use FILE *'s.).  fflush() does performs quite
a bit of error checking including: checking the underlying file
descriptor to see if it is open, checking for POSIX compliance,
checking to see if there is something in the buffer, checking the last
operation performed on the stream, etc.  So, there will be overhead
when calling fflush(), even if the buffer does not currently require
flushing.
.Generally, you would only need to call fflush() after fwrite() if it
is not certain that the buffer will be flushed by the fwrite() call.
It is usually safest to call fflush() after each fwrite() just in case
the buffer is not flushed due to the conditions for flushing not being
met.
.You would not normally need to call fflush() after fread() unless you
wanted to update the st_ctime and st_mtime whenever a fread had been
performed.  This would not usually be the case and could potentially
cost you some performance.
.I am not sure how SGI handles fwrite(), fread(), and fflush() so I
cannot comment on that.
.As a side note, fwrite() would generally be faster than write().
write() must always convert to kernel-mode when writing out the data.
fwrite(), on the other hand, only converts to kernel-mode when its
buffer is being flushed.  This causes less overhead with the smaller
number of mode switches.
.Question:
.Thanks for the information.  I'll try to digest this and
see how it applies to our application.  Since you say that
fwrite will flush the buffer if the input contains a newline,
does that imply that the input is scanned a character at a time
looking for a newline character?  Since we are writing binary
data, not text, this would we undesirable for our application;
we include a 'b' to indicate binary data in the Type argument
passed to fopen, but the man page for fopen says that the 'b'
is ignored.
.Response:
.The buffer is scanned for the newline a character at a time, but only
if _IOLBF (Line Buffering) has been set.  By default, _IOLBF is not
set when you open a file with fopen().  The only way to activate line
buffering on a stream is with either the setvbuf() or setlinebuf()
subroutines.  There are essentially three modes that a stream can be
set to:
.  _IOFBF  Causes I/O to be fully buffered. (default)
  _IOLBF  Causes output to be line-buffered.  The buffer
          is flushed when a new line is written, the buffer
          is full, or input is requested.
  _IONBF  Causes I/O to be completely unbuffered.
.See "setbuf setvbuf, setbuffer, or setlinebuf Subroutine" in Info
Explorer for more information.
.Yes, AIX does ignore the "b" flag on fopen, but this does not effect
the way that fwrite() works.
.So, if you use fopen() to open the file and do not change the mode of
the stream, the stream will be flushed only when the buffer is full.


Support Line: How does fwrite() and fflush() work? ITEM: AN7235L
Dated: September 1995 Category: N/A
This HTML file was generated 99/06/24~13:30:26
Comments or suggestions? Contact us