Introduction

Optimising, sometimes called tuning or fine tuning is the art of making computer programs run faster. It is
also the art of finding the optimum tradeoffs between ease of maintenance,
simplicity, terseness, speed, portability, low RAM (Random Access Memory)
usage and quickness to get the app working in the first place. Don’t go crazy
with optimisation. If you spend an extra man-week on a project optimising, over the
lifetime of the project, you had better save at least a man-week of
other people’s time waiting for the program and you must consider that you
have deprived those same people of using your program a week earlier.

Further, the cost of programmer time is rising and the cost of hardware is
dropping. Spending time on optimising is less and less justifiable over time.
However, it is often possible with a single tweak to double the speed of a program in
under an hour. It makes so sense to ignore such low-hanging fruit.

Wise Thoughts

We should forget about small inefficiencies, say about 97% of the time: premature optimization is the root of all evil.
~ Donald Ervin Knuth(born: 1938-01-10 age: 77)

A fast program is not as important as a correct one.
~ Steve McConnell

The only result of optimization you can usually be sure of without measuring
performance is that you’ve made your code harder to read.
~ Steve McConnell

Jackson’s rules of Optimization:

Don’t do it.

(For experts only) Don’t do it yet — that is until you have a
perfectly clear and unoptimized solution.

~ M.A. Jackson

Optimisation has become a dirty word. I have even been chastised for
thinking about which of two equally easy and easy-to-maintain ways of
doing something is more efficient. The problem is premature
optimisation, not optimisation itself. The problem is about trading off ease of
maintenance with speed. When the fast code is also the simplest and easiest to
maintain, you are an idiot to avoid it, at any stage.

~ Roedy
Green(born: 1948-02-04 age: 67)

How to Proceed

If you have a program that is running too
slowly, here is what to do. I suggest following the steps in this order:

Get someone experienced in these matters to look over your code and find out
where it is spending all its time. It helps to have a fresh pair of eyes on the
problem. They will use a profiler to
determine this. This identifies the bottlenecks.

Look at the bottleneck code for obvious blunders and correct them.

See if there is a totally different faster algorithm for accomplishing the same
thing, e.g. HashMap vs linear searching a long table.

Clean up the bottleneck code to ship shape. This is not considered optimising,
just getting it squeaky clean with no rambling.

See if the program is recomputing anything. Perhaps it can avoid redoing work
it has already done. The form of optimisation tends to make the code more complex
and less maintainable, but not always. Sometimes inexperienced programmers call
methods where they are not needed at all and that code can be yanked out
entirely.

If all else fails try JNI with
C/ C++. Beware.
JNI (Java Native Interface) only saves you CPU (Central Processing Unit)
time if you can do a substantial amount of work in C/C++ on each call to
offset the high overhead of the call.

If even that fails try JNI with assembler.

If that fails, tell your customers they will need bigger iron.

What Not To Do

Fools do by hand what a smart compiler will do for you automatically. This just
makes the code harder to read and maintain.

Fools spend hours and hours fine tuning a method that is used only once per
session. There is not much point in optimising anything other than the innermost
loops. Typically 80% of the execution stays confined to
20% of the code.

Fools carefully shave every nanosecond off a BubbleSort implementation. You are
much better off to seek out a better algorithm that scales well and even better to
find a canned solution written by some expert on the matter.

Fools buy a new computer twice as expensive as the old one just so that some
program will run decently fast. Usually fine tuning just a few methods in the
inner-most loops will give you a huge boost in speed. Consider judicious use of
native methods in C or assembler, considering
however the huge Java overhead to call them.

Fools fail to encapsulate so that if it turns out a technique is too slow, it
means a total rewrite to substitute a more efficient one.

Fools are great believers in NIH (Not Invented Here)
syndrome. Instead of using canned solutions provided by others, fools rewrite
everything in a custom fashion to gain a tiny increment in execution speed.

Fools don’t measure the results of their optimisations to ensure they
actually speed things up. Converting a huge Java method to a few
lines of assembler can actually slow it down!

Fools don’t think about the tradeoffs. Highly optimised code is usually
less portable, harder to maintain and of course takes much longer to write. It
should be used sparingly and judiciously.

Fools throw away the unoptimised Java versions of a method they re-implement in
assembler, or they keep it and fail to keep it up to date. It never occurs to them
that Java version may come in very handy to port the code to new platforms or to
help document the assembler code. These simple, correct, but slow implementations
are also useful in testing out the faster versions to make sure they give the
correct results on millions of test cases.

Fools scatter their optimised code all over. Try to put together code that
needs to be tweaked for a particular platform in one class. There is then a better
chance it will be reoptimised for other platforms.

Fools go for long-winded solutions. Simple, clear, terse ones are easier to
understand, easier to maintain and usually run the fastest on some other
platform.

On the other hand fools go for ultra-terse solutions without regard for how
long they actually take to execute. They make the unwarranted assumption that
shorter source always means faster code.

Fools are totally unaware of how the various system libraries work inside. They
have no idea which ones are best suited for various circumstances. When it would be
equally easy to code something two different ways, they have no idea which would
likely run faster and may choose a technique orders of magnitude slower that may
later need to be rewritten.

Fools rampantly go for speed totally ignoring RAM
usage. To their dismay they find their programs sometimes run even slower because
of swapping and more frequent and difficult garbage collection.

General Tips

The sorts of thing you can legitimately
do are:

Avoid creating new objects unnecessarily. Think about how to reuse old ones,
e.g. pass in a return object as a parameter rather than having a method create a
brand new one.

Make sure you nullify pointers when you no longer need objects freeing them up
for garbage collection.

Make sure you don’t reinvent the wheel. Use standard library code. It
will gradually be tuned by clever people to use the latest and greatest algorithms
without further effort on your part, e.g. sort, Collections.

If Swing is the problem, you might consider redoing the
GUI (Graphic User Interface) in IBM (International Business Machines)
’s SWT.

A Touch of Cynicism

In the end, what counts in the marketplace
is the psychological impression a program gives to its users.

You can speed up a program by wasting even more cycles
with progress bars and animated spinners — the approach pioneered by
Microsoft.

If the program behaves what appears to behave like a reluctant or lazy human, it
will be perceived as slow. If a program appears industrious, it can actually be a pig
and get away with it.

In GUI s, what most
often counts most is some sort of rapid acknowledgement feedback not the total
elapsed time of some process.

Garbage Collection

It is quite common to discover the problem is frequent and lengthy garbage
collections. Understanding the problem requires a book just in itself. However, here
are a few rules of thumb:

Avoid holding on to a String or Object any longer than necessary. Ideally all references to an
Object will automatically go out of scope when you no longer need it, but if they
don’t, nullify the references when you no longer need them. The more
references you have, the more RAM you chew up and the more frequently garbage
collection will happen and the more objects garbage collection has to
recover.

Be aware that if you have a reference to a tiny substring of a giant
String (e.g. the contents of a file read into RAM), you
are pinning that entire giant String in RAM. It cannot be
garbage collected. Sometimes, it may pay to use newString to disencumber it.

The java.exe run time has defaults for the amount of
virtual RAM it will use for various purposes and command-line parameters to
override them. The current settings may be wastefully large, or ridiculously
parsimonious. The optimum size depends on your app, how much RAM you bought, what
else is running, which GC (Garbage Collection) algorithm you have selected, whether you have 32 or
64-bit OS/Java…

Compiler and Run Time Optimisation

Modern compilers and run times to some pretty spectacular optimisation without you
having to do any work. For example, code that is not marked final will be inlined. Only later if some overriding class is loaded
will the inlining be dynamically undone. Jet does loop versioning. If you had a loop
that for example kept checking if it were an odd or even iteration, Jet would split
the code into two versions, one for odd and one for even and do the test only once
at the top.

Optimisations I would like to see:

an interface reference promoted to a specific class reference if the optimiser
is fairly sure of what class will actually be referenced.

Monitoring runs to figure out the optimal initial values for StringBuilder allocations.

In processes where a char[] in turned into a
String, figure out a way to insert the char[] into a
String without allocating a new buffer and copying it.

The cover image is not a pair of rabbits as I first thought. It covers the Java Performance Toolbox, Working with the JIT Compiler, Garbage Collection, Heap Memory Best Practices, Native Memory Best Practices, Threading and Synchronization Performance, Java Enterprise Edition Performance, Database Performance Best Practices, Java SE API Tips, and tuning flags.

Charlie Hunt is the JVM performance lead engineer at Oracle. He is responsible for improving the performance of the HotSpot JVM and Java SE class libraries. He has also been involved in improving the performance of the Oracle GlassFish and Oracle WebLogic Server. This also covers the JVM’s internal architecture (including its memory model and garbage collectors) and the art of benchmarking and performance testing.

A set of 95 short programs that give astonishing results. When you understand them, you understand the quirkier features of Java. Bloch wrote much of the JDK class library. He also wrote the Effective Java Programming Language Guide

No design patterns, just generic advice on good Java programming style. This is considered the best explanation of generics, even though it has just one chapter on generics. People claim it all came clear after reading his explanation. It is also consider the best explanation of serialization. Not to be confused with his earlier Effective Java Programming Language Guide. book website