When the Java application is highly polymorphic (complex class hierarchy), then it is more efficient to
use abstract types, rather than interface types, as interface calls are more expensive. For interface
calls with few implementations, the TRJIT compiler attempts to use an if-then-else chain (called
Polymorphic-Inline-Cache) [8] to compare the receiver types for faster interface dispatch. Figure 8
shows an example.
o = receiver object ;
x = receiver class (o) ;
if ( x == expected-class-1 )
x.foo(a, b, c) ;
// implementation of foo from class-1
else if ( x == expected-class-2)
x.foo(a, b, c) ;
// implementation of foo from class-2
else if ( x == expected-class-3)
x.foo(a, b, c); // implementation of foo from class-3
else
o.foo(a, b, c); // checks failed, interface call
Figure 8. Example of Polymorphic Inline Cache
Guideline: Use exceptions and reflection rarely
To provide the best possible performance of the application in the common cases, the VM and the JIT
optimize the non-exception paths of the application in favor of the exception paths. Throwing an
exception is expensive because of the associated runtime overhead. Therefore, exceptions should
not be used for normal flow-of-control in a Java application.
The Java reflection API provides a simple interface for you to obtain information about various
classes, methods and objects of the application when it runs in the JVM. However, reflection
introduces additional levels of abstraction and indirection that affects application performance. You
should not use reflection in the core of a performance-critical application. Figure 9 and Figure 10
show an example and the performance cost that is associated with using reflections.
IBM Just-In-Time Compiler (JIT) for Java
Best practices and coding guidelines for improving performance
public int invokeDirect() {
int sum = 0;
for (int i = 0; i < N; i++) {
sum = increment(sum);
}
return sum;
}
public int invokeReflect() {
try {
Class c = Class.forName("reflectTest");
Method m = c.getDeclaredMethod("increment", new Class[]{int.class});
//
create
the
args
array
Object [] args = new Object[1];
//
init
the
sum
Object sum = new Integer(0);
for (int i = 0; i < N; i++) {
args[0]
=
sum;
sum
=
m.invoke(this,
args);
}
return
((Integer)sum).intValue();
} catch (Exception e) {
System.out.println(e);
}
return 0;
}
Figure 9: Example of using reflection
Loops
Loops are an integral part of the Java programming language. For many programs, a good majority of the
application time is often spent executing loops. As a result, TRJIT employs many optimization techniques
that are intended to aggressively improve loop performance in Java. You can assist the TRJIT compiler
by using the following guidelines to ensure that their loops are well-behaved.
Guideline: Do not modify the loop bounds within the loop body
When the loop bounds are independent of the loop body, the TRJIT can usually profile the bounds
and create specialized, more optimized versions of the loop. Furthermore, the ability to predict and
compensate for loop exits is important for many fundamental loop optimization techniques, such as
loop versioning and loop unrolling. If the loop bounds were modified within the loop body, such loop-
optimization techniques are typically not applicable, which results in poorer performance.
IBM Just-In-Time Compiler (JIT) for Java
Best practices and coding guidelines for improving performance
Figure 10: Performance of Reflection example in Figure 9
Figure 11 shows an example of a poorly behaved loop with a loop bound ‘j’ that is modified within the
loop.
public void badLoopBounds() {
int i = 0;
while (i < j) {
...
j = newLoopBounds(); // Modifying of loop bounds
i++;
}
}
Figure 11. Example of a poorly behaved loop bound - The loop bounds: j is modified within the loop by using the
newLoopBounds() method
IBM Just-In-Time Compiler (JIT) for Java
Best practices and coding guidelines for improving performance
Figure 12 shows a well-behaved version, where the loop bound is invariant. The JIT will be able to
optimize the latter loop better.
public void goodLoopBounds() {
int i = 0;
while (i < N) {
// Loop bounds N is never modified
...
i++;
}
}
Figure 12. Example of a well-behaved loop bound - The loop bound: N is never modified within the loop
Guideline: Increment the loop index by a single value across all paths
Similar to the previous guideline, many loop-optimization techniques depend on well-behaved loop
iterations and the prediction of the number of iterations a loop will run. If two paths in the loop body
increment the loop index by different amounts, many TRJIT loop-optimization techniques cannot be
applied to improve the loop’s performance.
Figure 13 shows a loop that increments the loop index by 1 or 2, depending on the condition of the
nested if-statement. For the JIT compiler to optimize the loop, it must prove certain properties of the
condition expression (for example, whether the condition expression is loop invariant), which might
not be possible.
public void badLoopIndexIncrements() {
for (int i = 0; i < 100; i++) {
// Loop Index may be incremented by 1 or 2
if
(condition)
i++;
else
i = i + 2;
}
}
Figure 13. Example of poorly behaved loop-indices increments - The loop index: i will increment by 1 or 2, depending
on the condition
Figure 14 shows a well-behaved loop that only increments the loop index by a single value. The JIT
recognizes this property and optimizes the loop accordingly.
public void goodLoopIndexIncrement() {
for (int i = 0; i < 100; i++) {
// Loop index incremented by 1 all the time
i++
}
}
Figure 14. Example of well-behaved loop-indices increments - The loop index: i will increment by the same value on
each iteration
IBM Just-In-Time Compiler (JIT) for Java
Best practices and coding guidelines for improving performance
Dostları ilə paylaş: |