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

  • Minimize accessing the hard disk.
  • Minimize accessing the underlying operating system.
  • Minimize processing bytes and characters individually.

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

  • Use buffering to minimize 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 With Buffering: yields better performance
    try { File f = new File(“myFile.txt”); FileInputStream fis = new FileInputStream(f); int count = 0; int b = 0; 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 operating system. try { File f = new File(“myFile.txt”); FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis); int count = 0; int b = 0; 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 (i.e. “\n”) 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);
    // To redirect standard output to a file instead of the “System” console which is the default for both “System.out” (i.e.
    // standard output) and “System.err” (i.e. standard error device) variables
    System.setOut(ps);
    while (someConditionIsTrue)
        System.out.println(“blah… blah…”);
    }

    It is recommended to use logging frameworks like Log4J with SLF4J (Simple Logging Façade for Java), which uses buffering instead of using default behavior 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 minimizing 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.
Tagged . Bookmark the permalink.

Leave a Reply