14.3 Inlining

An important optimization technique is inlining. In inlining, an invoke instruction is replaced by the code that is called. This saves the overhead of having to push the arguments onto the stack, create a new stack frame, and handle the result.

For example, you may want to inline the methods width and height in area:

 class Rectangle {    float x1, y1, x2, y2;    final float width()    {        return x2 - x1;    }        final float height()    {        return y2 - y1;    }    float area()    {        return width() * height();    } } 

The unoptimized version of area is

 .method area()F aload_0 invokevirtual Rectangle/width ()F aload_0 invokevirtual Rectangle/height ()F fmul freturn .end method 

It may take even more time to perform the method invocation than it does to perform the subtraction. The overhead of the method invocation can be eliminated by including the code for width and height in the definition for area:

 .method area()F aload_0 getfield Rectangle/x2 F aload_0 getfield Rectangle/x1 F fsub aload_0 getfield Rectangle/y2 F aload_0 getfield Rectangle/y1 F fsub fmul freturn .end method 

Note that this can be done only if the methods are marked final or if the entire class is final. Otherwise, somebody might redefine the methods in a subclass:

 /** A rectangle that can be angled, defined by 3 points:  *                  (x1, y1) ------------- (x2,y1)  *                           /           /  *                  (x3, y3) ------------- (x3+x2-x1, y3+y2-y1)  */ class Parallelogram extends Rectangle {    float x3, y3;    float width()    {        return Math.sqrt((x1-x3)*(x1-x3) + (y1-y3)*(y1-y3));    }    float height()    {        return Math.sqrt((x2-x1)*(x2-x1) + (y1-y2)*(y1-y2));    } } 

Methods that are static are also good candidates for inlining, since they cannot be overridden in subclasses.

Sometimes the optimizer can prove the exact runtime type of an object. For example, if the object was just created with a new instruction, then the optimizer knows exactly what type of object is on top of the stack. The optimizer can treat this object as if it were a member of a final class, since it can be sure that the methods won't be overridden.

A JVM implementation may be able to inline methods that are not final if it has reason to believe that the nonfinal methods are never actually overridden. This may occur if there are no subclasses of the class with the method. If new subclasses are introduced later, the JVM may have to undo some of its optimizations.

14.3.1 Inlining Fields

Inlining can also be done with final fields, replacing a reference to the field with its value. This is basically identical to constant propagation (section 14.2.2).

This example shows a field that can be inlined:

 class Teapot {    /** Brewing time is 3 minutes, or 180,000 milliseconds */    public final static long TIME_TO_BREW = 180000;    void makeTea()    {         // Make tea, then let it steep         Thread.sleep(TIME_TO_BREW);    } } 

The value TIME_TO_BREW can be substituted into the method makeTea:

 .method makeTea()V ;; Make tea, then let it steep ldc2_w 180000 invokestatic java/lang/Thread/sleep(J)V return .end method 

The ldc instruction replaces the much slower instruction

 getstatic Teapot/TIME_TO_BREW J 

Since the TIME_TO_BREW is final, it can't change while the program is running. However, this can be done only within the same class.

Consider the class TeaParty:

 class TeaParty {    void chat()    {         // Chat while the tea brews         Thread.sleep(Teapot.TIME_TO_BREW);    } } 

It is tempting to optimize chat in the same way makeTea is optimized. The Java language forbids this optimization between classes. This injunction applies to both fields and methods. Suppose somebody who liked stronger tea came along and changed the definition of Teapot so that TIME_TO_BREW was set to 200000. This would require recompiling TeaParty, but since nothing in Teapot references TeaParty, the compiler may not know it has to recompile.

14.3.2 Separate Compilation

The ability of a Java program to respond correctly even when other classes change is called binary compatibility. The Java Language Specification devotes all of chapter 13 to binary compatibility between class files.

While the compiler may not perform this optimization, the same sort of optimization may be performed by the JVM implementation itself. Once the classes are loaded into the JVM, they may not change. This allows the JVM implementation to make optimizations that a Java compiler may not make.

Most other languages do not have this restriction. They may require that all related classes be recompiled when any one class is changed. The C and C++ languages are examples.

Other languages are silent on the issue, which means that a compiler designer may choose when compilation must be done. The decision affects users of your compiler who may find themselves either compiling more frequently than they would like or encountering mysterious errors and saying to themselves, "I thought I changed that!" It also affects users of the resulting programs, who might appreciate the performance improvements of your optimizing compiler.



Programming for the Java Virtual Machine
Programming for the Javaв„ў Virtual Machine
ISBN: 0201309726
EAN: 2147483647
Year: 1998
Pages: 158
Authors: Joshua Engel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net