JavaOne Notes: Achieving Great File I/O Performance
Basic mom-and-apple-pie presentation on I/O, spent a long time warming up the basic concepts and the Java APIs. A good presenter from BEA with a few learnings shared, but nothing shocking. About an hour w/ the file I/O javadocs and a really good book that evaluates the actual performance internals and you likely would have captured the same.
What to think about when doing file I/O
� Data access pattern (rand, seq)
� Data size read/write at once
� How much of the file will be read/written
� How durable must the disk writes be (OS crash, power failure)
Writing Safely
� OS normally caches all disk writes (data may be lost in the event of OS crash)
� Most disks also cache all disk writes (data lost on power failure)
� APIs are available that let you �force� the I/O
� They make sure data is not cached by the O/S or disk
� These APIs do not always work!
o Depends on the O/S, filesystem, disk
o For IDE and Firewire it depends on hardware, device driver, and config settings for the driver
o Enterprise (SCSI, etc) hardware usually avoid these problems
General I/O concept: Bigger is better
� A few large I/Os are faster than many small I/Os
� There is overhead in traversing all the layers between app and disk
� Java -> JNI -> system call -> OS -> file system -> cache -> driver -> device
General I/O concept: Extending files is slow
� When you just write data to existing file, you just write to the disk blocks where your data goes
� When you write past the EOF, OS must keep track of new file size
� When you must extend, try doing it in larger chunks
FileOutputStream
� Easy to use � everyone knows paradigm
� Only supports sequential IO
� File IO may be easily buffered using BufferedOutputStream
� Limited force options!
� Suggestion: use BufferedOutputStream for buffering
o But don�t forget to close or flush when you�re done!
o Application exit doesn�t close/flush!!!
RandomAccessFile
� Totally random IO
� Supports all force options
� Suggestion: buffer your IO if you can to increase throughput, however you have to do this in your own code!
FileChannel
� You get a FileChannel from RandomAcceessFile
� Supports all force modes
� Implements the Channel interface(s)
o Takes NIO byte buffers as parameters
� Supports �transfer� � transfer a file to a socket; at the OS kernel level for Linux transfer data from a socket to a file, vice-a-versa (abstracted in Java)�very fast on those platforms!
� Suggestions:
o Performance issues for all small writes in some JVMs
o Solution: allocate a single direct ByteBuffer and copy to and from here
� ByteBuffer is a simple wrapper object around a byte array
� DirectByteBuffer doesn�t allow you access byte array directly...useful for JNI native code integration; downside is access time to DirectByteBuffer is slow�allocation and garbage collection is also much more expensive
� If you arfe using the regular ByteBuffer the performance on Sun JVMs doesn�t seem to be as good as RandomAccessFile (currently), something is happening in the JVM
MappedByteBuffer
� Created from a FileChannel
� Very convenient to map a file into memory; can be extremely fast IF you can use it
� Creates a big ByteBuffer containing your file
� You may explicitly force updates to disk
� Fatal flaw in Java no way to quickly unmap a file; mapping large files into a 32-bit address space JVM may start giving out of memory errors to other allocations in your app
� Handy class to use IF the amount of data is significantly small OR if you are on a 64-bit OS
� Suggestions:
o Use to randomly read and write small files if data integrity is unimportant
o Be very careful on 32-bit JVMs
o Don�t use too many�there�s no �close� call!!!!
o Believe that the file is unmapped during finalizer, but NO promises here!
Reliable Disk IO
� IO writes may be forced explicitly
� RandomAccessFile.flush()
o Forces contents AND meta-data
o Equivalent of Unix fsync()
� FileChannel.flush()
o Forces contents and OPTIONALLY meta-data
o Equivalent of fsync() or fdatasync()
� Meta-data is the last mod time, etc.; modifying means disk writes have to occur in 2+ places on disk
� Ignoring meta-data sync is slightly faster
� Or implicitly:
� RandomAccessFile �rws� mode
o Forces contents and meta-data
o Equivalent of open(O_SYNC)
� RandomAccessFile �rwd� mode
o Forces contents only
o Equivalent of open(O_DSYNC)
� If you can use �rwd� mode does the write in one system call vs multiple trips to write file and file meta-data.
� Implicit vs explicit puts control in your hands
Reliable Disk Writes
� Example: transaction log�reliable messaging log
� Possible solutions:
o Call �flush� after each write
o Open file with a �sync� flag like �rwd� or �rws�
o Use a memory mapped file and call �force�
� Results:
o Using the �rwd� mode will be fastest
o flush() was 2x map force for write sizes from 50-4096 bytes; rwd was 50%-300% faster (smaller writes much faster obviously when buffered internally) than flush()
o flush() and map means many JVM -> OS system calls
RandomReads
� Program: big file/database of several 100MB
� Randomly looking at bits and pieces
� Possible solutions:
o Use standard file IO APIs
o Use memory mapping
� Results
o Memory mapping (particularly for smaller reads) is tremendously faster
o If you can deal w/ the address space issues w/ memory mapping you can get phenomenal read speed
Direct I/O: Things you can�t do in Java
� Every O/S today has a feature called �direct I/O�
� Bypasses O/S buffer cache, generally reducing CPU usage for large I/O
� Designed for use by databases
� There is no Java API for this, so we must use native code�
� His example app he got 2x the I/O output rate (MB/sec), however stated his tests were a bit old
Q&A
� Guidelines for choosing buffer sizes on BufferedOutputStreams? No great suggestion other than make them big as possible; you�d actually have to do some performance tests
� How do these techniques apply to network I/O? I don�t know; certainly buffering data applies to network I/O
� Will this also apply to SAN and solid-state devices? To some degree, remember minimizing trips to kernel and file system still apply! I don�t have benchmarks.