A lot of ‘performance tests’ are posted online lately. Many times these performance tests are implemented and executed in a way that completely ignores the inner workings of the Java VM. In this post you can find some basic knowledge to improve your performance testing. Remember, I am not a professional performance tester, so put your tips in the comments!
For example, some days ago a ‘performance test’ on while loops, iterators and for loops was posted. This test is wrong and inaccurate. I will use this test as an example, but there are many other tests that suffer from the same problems.
So, let’s execute this test for the first time. It tests the relative performance on some loop constructs on the Java VM. The first results:
Iterator – Elapsed time in milliseconds: 78
For – Elapsed time in milliseconds: 28
While – Elapsed time in milliseconds: 30
Allright, looks interesting. Let’s change the test a bit. When I reshuffle the code, putting the Iterator test at the end, I get:
For – Elapsed time in milliseconds: 37
While – Elapsed time in milliseconds: 28
Iterator – Elapsed time in milliseconds: 30
Hey, suddenly the For loop is the slowest! That’s weird!
So, when I run the test again, the results should be the same, right?
For – Elapsed time in milliseconds: 37
While – Elapsed time in milliseconds: 32
Iterator – Elapsed time in milliseconds: 33
And now the While loop is a lot slower! Why is that?
Getting valid test results is not that easy!
The example above shows that obtaining valid test results can be hard. You have to know something about the Java VM to get more accurate numbers, and you have to prepare a good test environment.
Some tips and tricks
- Quit all other applications. It is a no-brainer, but many people are testing with their systems loaded with music players, RSS-feed readers and word processors still active. Background processes can reduce the amount of resources available to your program in an unpredictable way. For example, when you have a limited amount of memory available, your system may start swapping memory content to disk. This will have not only a negative effect on your test results, it also makes these results non-reproducible.
- Use a dedicated system. Even better than testing on your developer system is to use a dedicated testing system. Do a clean install of the operating system and the minimum amount of tools needed. Make sure the system stays as clean as possible. If you make an image of the system you can restore it in a previous known state.
- Repeat your tests. A single test result is worthless without knowing if it is accurate (as you have seen in the example above). Therefore, to draw any conclusions from a test, repeat it and use the average result. When the numbers of the test vary too much from run to run, your test is wrong. Something in your test is not predictable or consistent. Try to fix your test first.
- Investigate memory usage. If your code under test is memory intensive, the amount of available memory will have a large impact on your test results. Increase the amount of memory available. Buy new memory, fix your program under test.
- Investigate CPU usage. If your code under test is CPU intensive, try to determine which part of your test uses the most CPU time. If the CPU graphs are fluctuating much, try to determine the root cause. For example Garbage Collection, thread-locking or dependencies on external systems can have a big impact.
- Investigate dependencies on external systems. If your application does not seem to be CPU-bound or memory intensive, try looking into thread-locking or dependencies on external systems (network connections, database servers, etcetera)
- Thread-locking can have a big impact, to the extent that running your test on multiple cores will decrease performance. Threads that are waiting on each other are really bad for performance.
The Java HotSpot compiler
The Java HotSpot compiler kicks in when it sees a ‘hot spot’ in your code. It is therefore quite common that your code will run faster over time! So, you should adapt your testing methods.
The HotSpot compiler compiles in the background, eating away CPU cycles. So when the compiler is busy, your program is temporarily slower. But after compiling some hot spots, your program will suddenly run faster!
When you make a graph of the througput of your application over time, you can see when the HotSpot compiler is active:
Througput of a running application over time
The warm up period shows the time the HotSpot compiler needs to get your application up to speed.
Do not draw conclusions from the performance statistics during the warm up time!
- Execute your test, measure the throughput until it stabilizes. The statistics you get during the warm up time should be discarded.
- Make sure you know how long the warm up time is for your test scenario. We use a warm up time of 10-15 minutes, which is enough for our needs. But test this yourself! It takes time for the JVM to detect the hot spots and compile the running code.
From Dries Buytaert I received a link to a paper called Statistically rigorous Java performance evaluation. I highly recommend reading it when you want to know more about measuring Java performance.
Remember, I am not a professional performance tester, so put your tips in the comments!