21. How can you improve Java I/O performance

Java applications that utilize Input/Output are excellent candidates for performance tuning. Profiling of Java applications that handle significant volumes of data will show significant time spent in I/O operations. This means substantial gains can be had from I/O performance tuning. Therefore, I/O efficiency should be a high priority for developers looking to optimally increase performance.

The basic rules for speeding up I/O performance are

  • Minimise accessing the hard disk.

  • Minimise accessing the underlying operating system.

  • Minimise processing bytes and characters individually.

    Let us look at some of the techniques to improve I/O performance.

  • Use buffering to minimise disk access and underlying operating system. As shown below, with buffering large chunks of a file are read from a disk and then accessed a byte or character at a time.


Without buffering : inefficient code

try{

File f = new File("myFile.txt");

FileInputStream fis = new FileInputStream(f);

int count = 0;

int b = ;

while((b = fis.read()) != -1){

if(b== '\n') {

count++;

}

}

// fis should be closed in a finally block.

fis.close() ;

}

catch(IOException io){}

Note: fis.read() is a native method call to the

underlying system.


With Buffering: yields better performance

try{

File f = new File("myFile.txt");

FileInputStream fis = new FileInputStream(f);

BufferedInputStream bis = new BufferedInputStream(fis);

int count = 0;

int b = ;

while((b = bis.read()) != -1){

if(b== '\n') {

count++;

}

}

//bis should be closed in a finally block.

bis.close() ;

}

catch(IOException io){}

Note: bis.read() takes the next byte from the input buffer and only

rarely access the underlying operating system.


Instead of reading a character or a byte at a time, the above code with buffering can be improved further by reading one line at a time as shown below:

FileReader fr = new FileReader(f);

BufferedReader br = new BufferedReader(fr);

While (br.readLine() != null) count++;

By default the System.out is line buffered, which means that the output buffer is flushed when a new line character is encountered. This is required for any interactivity between an input prompt and display of output.

The line buffering can be disabled for faster I/O operation as follows:

FileOutputStream fos = new FileOutputStream(file);

BufferedOutputStream bos = new BufferedOutputStream(fos, 1024);

PrintStream ps = new PrintStream(bos,false);

System.setOut(ps);

while (someConditionIsTrue)

System.out.println(“blah…blah…”);

}

It is recommended to use logging frameworks like Log4J or apache commons logging, which uses buffering instead of using default behaviour of System.out.println(…..) for better performance. Frameworks like Log4J are configurable, flexible, extensible and easy to use.

  • Use the NIO package, if you are using JDK 1.4 or later, which uses performance-enhancing features like buffers to hold data, memory mapping of files, non-blocking I/O operations etc.

  • I/O performance can be improved by minimising the calls to the underlying operating systems. The Java runtime itself cannot know the length of a file, querying the file system for isDirectory(), isFile(), exists() etc must query the underlying operating system.

  • Where applicable caching can be used to improve performance by reading in all the lines of a file into a Java collection class like an ArrayList or a HashMap and subsequently access the data from an in-memory collection instead of the disk.


4 comments: