Make your Java Application run faster - Part 2 - General Optimization Techniques

In the last part of the series “Compiler Optimizations” we saw some common optimizations done by the java compiler. You need to be aware of these optimizations because the compiler does most of the optimizations for you. In this part of the series, we shall see some optimizations that you can do to your code. These optimizations are general and apply to any programming language.

Strength Reduction: Using cheaper/faster operations in place of expensive ones.

For example, use of the compound assignment operators such as a+=b instead of a=a+b will be faster because they result in fewer byte code instructions.

You can also use shifts instead of multiplication by powers of two, for example, x >> 2 can be used in place of x / 4, and x <<>

Common Sub Expression Elimination: Eliminate redundant calculations in your code by performing those computations only once and storing the result in a temporary variable, then replacing the computations with the temporary variables

For Example: Instead of writing

double p = d * (average / total) * scorep;

double q = d * (average / total) * scoreq;

The common sub expression can be calculated once and used for both calculations:

double depth = d * (average / total);

double p = depth * scorep;

double q = depth * scoreq;

Code Motion: It is similar to sub expression elimination. Here, expensive operations can be moved out of a loop so that it is evaluated only once. We had seen this in compiler level optimizations also, but compiler can perform code motion only for simple expressions- involving only local and final static variables. For example in

for (int i = 0; i <>

x[i] *= computeSinVar(y);

We can move computeSinVar(y) out of the loop because y does not vary inside the loop.

Double sinVar = computeSinVar (y);

for (int i = 0; i <>

x[i] *= sinVar;

Compiler cannot do this optimization automatically because it does not know whether the array x is being modified in the method computeSinVar.

Un Rolling Loops: Reduce the number of iterations of a loop by performing more operations in each iteration of the loop.

The idea is to save time by reducing the number of overhead instructions that the computer has to execute in a loop.

To achieve this, the instructions that are called in each iteration of the loop are replicated to form a single sequence.

There will be significant performance gain only if the operations done in the loop consists of only simple statements such that the loop control consumes a significant part of each iteration.

For Example:- The loop in the previous example can be unrolled as below.

Double sinVar = computeSinVar (y);

for (int i = 0; i <>

{

x[i] *= sinVar;

x[i+1] *= sinVar;

}


In practice, unrolling loops such as this -- in which the value of the loop index is used within the loop and must be separately incremented -- does not yield an appreciable speed increase in interpreted Java because the bytecodes lack instructions to efficiently combine the "+1" into the array index. Moreover, in the above example the loop will work fine only if the array x has even number of elements.

Note that some programming language compilers like c unroll loops as a part of the compiler optimization itself.