When and How to improve Java performance

header: java performance

When to increase performance (and when not to)

Most of the time, there is a trade-off: You can either have fast code, or you can have clean code. And most of the time, clean code is more important. but there are cases when speed actually matters and one is willing” to sacrifice quality and maintainability of the code for an performance increase.

Generally, clean, readable code is a lot more important than speeding up the whole code by some percentage with dirty hacks. But if a function is called a lot (depending on function size from a couple thousand to a couple million times) performance might be more important. In that case: comment it. A function that nobody understands just calls for bugs later on.  Here are some tips and ideas on performance increase in Java and how to achieve it. But remember: Premature optimization is the root of all evil.

General tips to Improve Performance of Java Code

  • find a better algorithm (really! if you find one, it is a lot more helpful than anything i write here).
  • profile and remove bottlenecks. Do not try to increase performance in code that is not a bottleneck. It is not worth destroying nice code for a performance increase of point-something percent.
  • Exceptions are only for exceptional cases. They are expensive because an exception object has to be created each time one is thrown, so they should not be used to implement logic.
  • use Java build in functions instead of writing your own function. also: use the CORRECT build in function. for example do not use + to concatenate lots of strings in a loop, but StringBuilder; use Java Collection depending on actions you perform on it, etc.
  • do not use objects when using primitives will get the job done.
  • reuse objects when possible. Do this by changing the values of an object/resetting an object instead of creating a new one. Another example where one can avoid creating objects: instead of:
    public void oftenCalledMethod() {
        g.setColor(new Color(100,100,100));
        // do something
        g.setColor(new Color(200,200,200));
        // do some more
    }
    save the color somewhere (in a local field or in an utility/options class) and reuse the color object.
  • If something is constant, declare it as static constant. Especially when it is big (like a list/array) and present in many instances. Static class variables are only created once while non-static variables have to be created for every object.

Increase Java performance in big loops

Reduce method calls to objects

Reducing method calls to objects is a good idea as calling a method is not for free (performance wise). For example, if the loop termination variable is stored in an object and you are sure that it does not change, save it in a local variable:

int size = myObject.getSize(); // size does not change and is really big
for (int i = 0; i < size; i++) {
    // do something
}

If size is just a constant, it probably does not matter as the compiler will inline it. But often it is not, for example when it returns the size of an internal list.

Rephrasing mathematical statements

If using mathematical operations, think about what they are doing and how expensive they are. As a rule of thumb: addition is faster than multiplication is faster than division is faster than exponent.

An example: instead of

int[] somethingToCalulate;
int constant;
for (int i = 0; i < giantSize; i++) {
    somethingToCalulate[i] = i * constant;
}

you might want to use:

int[] somethingToCalulate;
int constant;
int accumulated = 0;
for (int i = 0; i < giantSize; i++) {
    somethingToCalulate[i] = accumulated;
    accumulated += constant;
}

 Calculate once, use as often as you like

A generalization of the above: If you are calculating things twice, stop that. Save the result and use that instead. This is generally a good idea, not only when performance matters as it generally results in cleaner code.

For example instead of:

int[] somethingToCalulate;
int[] somethingOtherToCalulate;
for (int i = 0; i < giantSize; i++) {
    somethingToCalulate[i] = i * (constant - i);
    somethingOtherToCalulate = i * (constant - i) * otherConstant;
}

One could use:

int[] somethingToCalulate;
int[] somethingOtherToCalulate;
for (int i = 0; i < giantSize; i++) {
    int temp = i * (constant - i);
    somethingToCalulate[i] = temp;
    somethingOtherToCalulate = temp * otherConstant;
}

In this example it is pretty obvious, but there are cases where the double calculation is not that easy to spot. If temp would be a constant factor (ie not depent on i or anything other that is done in the loop) calculate it outside the loop.

Unrolling Loops

I found that unrolling loops does not help to increase speed, but it definitely makes it harder to read and maintain.

Netbeans Profiler

The Netbeans profiler may be used to identify and localize performance problems. This makes it easier to resolve them.

Starting the Profiler

netbeans profiler

The Netbeans Profiler is easy to use and yet quite informative. to start it, just press the profile button (marked in the screenshot to the left). If you do not have such a button go to tools -> plugins and search for profiler. Then install it and the button should be there.

If your program does not need user input just wait until it finished and then view the snapshot. If the program does need user input supply enough of it before exiting the program. If you want to profile something  where you have influence on the size (for example a program that performs some action on a list of points) choose a size that is neither too small nor too large. The profiler causes an overhead and if your list is too big the program will take forever to finish. However, if it is too small the data the profiler produces will not be helpful.

Interpreting the results of the profiler

netbeans profiler call tree example

After the program is finished, you will receive a snapshot similar to the one on the left. The method that starts a thread/program naturally takes 100% of the execution time. If you press on the little plus left of it, you will see which methods are called and how much time they each take. If you find a method that takes too much time, inspect it. What is the self time of the method? Is that time appropriate or could it be reduced? What about the methods that it calls? Is it possible to reduce the number of times they are called? What about their performance? Answer these questions and you are half way to fixing your performance problems.

netbeans profiler hotspots example 

In my example, you can see that the method alphaBeta is listed inside another method alphaBeta. So obviously the method calls itself recursively. A way to easier identify bottlenecks in case of an recursive function is to view the Hot Spots offered by the Netbeans profiler. An example of this can be seen to the left.

Further Reading

Simplenotions: Speed-up your java code
glenmccl: Thirty Ways to Improve the Performance [pdf]

The book Code Complete has a very nice chapter about improving the speed of java code (otherwise the book is all right as well, but this chapter really is excellent and the main reason I bought the book).

One thought on “When and How to improve Java performance

Leave a Reply

Your email address will not be published.