In my consulting practice, one of the most common complaints I hear about Java is that it's "too slow," "too resource intensive," or that "performance may be a problem." These statements are often made without any basis in fact; many assume that Java's poor performance is a given.
Of course, Java programs can be slow, memory intensive, and painful to use; but so can programs written in C++, Visual Basic, Smalltalk, Pascal, Ada, or C#. That doesn't necessarily mean that the language or the runtime environment is bad; it might just mean that a developer wrote the code without considering performance side effects. A well-written Java program usually performs just as well as a program written in any other programming language, and it often performs better, especially with the performance improvements in JDK 1.3 and 1.4.
One important note: Perceived performance is the critical measure. It doesn't matter how fast, efficient, or elegant your code is if it doesn't appear to be running or displaying fast enough. Likewise, ugly code can sometimes be blazingly fast—but a maintenance nightmare. We should really strive for code that combines the best of both worlds: it's elegant and performs well enough to satisfy our clients.
To move you a step closer to that goal, in this article, I walk you through a performance improvement process. To illustrate
the process, I include code that uses classes from the new java.nio package to provide you with additional clues about how to use them more effectively.
Suppose you've written or inherited a Java application that just isn't performing up to snuff. Your boss is breathing down your neck, and the delivery deadline is next week. What do you do?
I'm assuming you don't want to take the "run-away-as fast-as-you-can" approach. You want to fix the performance problems. Where do you start?
I recommend the following performance improvement process:
To illustrate the process, let's look at an example. Along the way, I'll fill in more details on the individual steps. The examples include mostly AWT (Abstract Windowing Toolkit) graphics, because performance improvements in graphical programs are easier to see and more interesting to write.
The following program reads a series of files and counts the occurrences of the letters a to z in the files. It displays a histogram of the letter frequency; the histogram updates after each file is read.
Examine the code below and note to yourself where you could improve it. Then follow along to see if your intuition was correct:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Frame;
import java.io.IOException;
import java.io.FileInputStream;
public class Letters extends Component {
long[] countArray = new long[26];
static char[] letterArray =
{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w','x','y','z'};
/**
* Find the number of occurrences of each letter of the
* alphabet in the named file. The result is returned
* as a 26-element array of long elements.
* Of course, this will only work for the English alphabet.
*/
void countCharacters (String filename)
throws IOException {
System.out.println ("...reading " + filename);
FileInputStream fis =
new FileInputStream (filename);
int tmp;
while ((tmp = fis.read()) != -1) {
char c = Character.toLowerCase((char)tmp);
int pos = c - 'a';
if ((pos >= 0) && (pos <= 25)) {
++countArray[pos];
}
}
fis.close();
}
/**
* Draw a histogram of the letter frequency.
* This method is triggered by repaint(), or by
* window manager repaint events.
*/
public void paint (Graphics g) {
long maxCount = 0;
for (int i=0; i<countArray.length; ++i) {
if (countArray[i] > maxCount) maxCount = countArray[i];
}
Dimension d = getSize();
double yScale = ((double)d.height) / ((double)maxCount);
int barWidth = (int)d.width / countArray.length;
int x = 0;
for (int j=0; j<countArray.length; ++j) {
g.setColor (Color.blue);
int barHeight = (int)(countArray[j]*yScale);
g.fillRect (x, d.height-barHeight,
barWidth, barHeight);
g.setColor (Color.white);
g.drawRect (x, d.height-barHeight,
barWidth, barHeight);
g.setColor (Color.black);
g.drawChars (letterArray, j, 1, x, 10);
x += barWidth;
}
}
/**
* The main method. Files to analyze are specified
* on the command line; their names are in argv.
*/
public static void main (String[] argv)
throws IOException {
Letters letters = new Letters();
Frame f = new Frame ("Letter Count");
f.add (letters);
f.setSize (300, 200);
f.setVisible (true);
long startTime = System.currentTimeMillis();
for (int i=0; i<argv.length; ++i) {
letters.countCharacters (argv[i]);
letters.repaint();
}
long endTime = System.currentTimeMillis();
System.out.println ();
System.out.println
("Elapsed time: " + (endTime - startTime) + " msec");
System.out.println();
for (int i=0; i<letters.countArray.length; ++i) {
System.out.print
(letterArray[i] + "=" + letters.countArray[i] + ",");
}
System.out.println();
}
}
What's wrong with this program, and how do we fix it? Let's follow our improvement process.