Are There Inline Functions in Java

Do modern Java compilers/JVM inline functions/methods which are called exactly from one place?

A Java JITC will attempt to inline any functions that appear (based on runtime statistics) to be called often enough to merit it. It doesn't matter whether the function is called in only one place or dozens of places -- each calling site is analyzed separately.

Note that the decision is based on several factors. How big the method is is one -- if there are a lot of potential inlining candidates only the most profitable will be inlined, to avoid "code bloat". But the frequency of the call (multiplied by the perceived expense of the call) is the biggest "score" factor.

One thing that will discourage inlining is obvious polymorphic calls. If a call might be polymorphic it must be "guarded" by code that will execute the original call if the arriving class is not the expected one. If statistics prove that a call is frequently polymorphic (and including all the polymorphic variants is not worthwhile) then it's likely not sufficiently profitable to inline. A static or final method is the most attractive, since it requires no guard.

Another thing that can discourage inlining (and a lot of other stuff) is, oddly enough, a failure to return from the method. If you have a method that's entered and then loops 10 million times internally without returning, the JITC never gets a chance to "swap out" the interpreted method and "swap in" the compiled one. But JITCs overcome this to a degree by using techniques for compiling only part of a method, leaving the rest interpreted.

What is method inlining?

Inlining is an optimization performed by the Java Just-In-Time compiler.

If you have a method:

public int addPlusOne(int a, int b) {
return a + b + 1;
}

which you call like this:

public void testAddPlusOne() {
int v1 = addPlusOne(2, 5);
int v2 = addPlusOne(7, 13);

// do something with v1, v2
}

the compiler may decide to replace your function call with the body of the function, so the result would effectively look like this:

public void testAddPlusOne() {
int v1 = 2 + 5 + 1;
int v2 = 7 + 13 + 1

// do something with v1, v2
}

The compiler does this to save the overhead of actually making a function call, which would involve pushing each parameter on to the stack.

This can clearly only be done for non-virtual functions. Consider what would happen if the method was overriden in a sub class and the type of the object containing the method isn't known until runtime...how would the compiler know what code to copy: the base class's method body or the sub class's method body? Since all methods are virtual by default in Java, you can explicitly mark those which cannot be overriden as final (or put them into a final class). This will help the compiler figure out that method will never be overriden, and it is safe to inline. (Note that the compiler can sometimes make this determination for non-final methods as well.)

Also, note the word may in the quote. Final methods aren't guaranteed to be inlineable. There are various ways you can guarantee a method isn't capable of being inlined, but no way to force the compiler to inline. It will almost always know better than you anyway when inlining will help vs. hurt the speed of the resulting code.

See wikipedia for a good overview of benefits and problems.

Java inline method that contains private calls

  1. in the beginning, there is source code
  2. the java compiler turns it into bytecode
  3. the jvm reads the bytecode, verifies its integrity, and interprets it. The bytecode of "hot spots" (frequently executed blocks of code) is just-in-time compiled into the instruction set of the CPU the JVM is running on

In Java, most optimization is deferred to the JIT-compiler in step 3 to benefit from statistics gathered during program execution. This includes inlining.

Access modifiers such as private are checked during verification, before the bytecode is interpreted, and before it is JIT-compiled. Since access modifiers have already been checked, the JIT is totally agnostic of their existence, and inlining a private method causes no difficulty.

BTW, if you want to check what the JIT does, you can use hotspot's -XX:+PrintAssembly to have the machine code translated back into assembly code for your inspection.

Call Kotlin inline function from Java

The inlining that's done by the Kotlin compiler is not supported for Java files, since the Java compiler is unaware of this transformation (see this answer about why reified generics do not work from Java at all).

As for other use cases of inlining (most commonly when passing in a lambda as a parameter), as you've already discovered, the bytecode includes a public static method so that the inline function can be still called from Java. In this case, however, no inlining occurs.

Inlining in Java

A couple of the other answers have suggested that only final methods can be inlined - this is not true, as HotSpot is smart enough to be able to inline non-final methods so long as they haven't been overridden yet. When a class is loaded which overrides the method, it can undo its optimisation. Obviously making the method final mean that's never required...

Basically let the JVM do its job - it's likely to be a lot better at working out where to inline than you are.

Do you have a situation where you're convinced that the JVM isn't doing a good job? Assuming you're using HotSpot, have you tried using the server version instead of client? That can make a huge difference.

Why Java doesn't have `inline` methods?

I don't see a reason why this happens. Try to the method final and the difference should go away. And use the latest version of Java from Oracle or IBM.

Now the reason why Java doesn't have many optimization keywords is simple: The people who invented Java didn't like how much time C developers spent to squeeze out the last drop of juice out of the compiler. That felt like a huge waste of productive time.

So the idea with Java is that you write code. You concentrate just on writing good code. The Java compiler and the runtime then make it fast. That means that the runtime analyzes the CPU and optimizes the code differently depending on what the CPU can do. This is why Java code can be faster than C++.

Of course, bad code is bad, no matter how clever the optimizer is. So you can write code which very slow but that doesn't depend on the language.

Would Java inline method(s) during optimization?

javac will present bytecode that is a faithful representation of the original Java program that generated the bytecode (except in certain situations when it can optimize: constant folding and dead-code elimination). However, optimization may be performed by the JVM when it uses the JIT compiler.

For the first scenario it looks like the JVM supports inlining (see under Methods here and see here for an inlining example on the JVM).

I couldn't find any examples of method inlining being performed by javac itself. I tried compiling a few sample programs (similar to the one you have described in your question) and none of them seemed to directly inline the method even when it was final. It would seem that these kind of optimizations are done by the JVM's JIT compiler and not by javac. The "compiler" mentioned under Methods here seems to be the HotSpot JVM's JIT compiler and not javac.

From what I can see, javac supports dead-code elimination (see the example for the second case) and constant folding. In constant folding, the compiler will precalculate constant expressions and use the calculated value instead of performing the calculation during runtime. For example:

public class ConstantFolding {

private static final int a = 100;
private static final int b = 200;

public final void baz() {
int c = a + b;
}
}

compiles to the following bytecode:

Compiled from "ConstantFolding.java"
public class ConstantFolding extends java.lang.Object{
private static final int a;

private static final int b;

public ConstantFolding();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public final void baz();
Code:
0: sipush 300
3: istore_1
4: return

}

Note that the bytecode has an sipush 300 instead of aload's getfields and an iadd. 300 is the calculated value. This is also the case for private final variables. If a and b were not static, the resulting bytecode will be:

Compiled from "ConstantFolding.java"
public class ConstantFolding extends java.lang.Object{
private final int a;

private final int b;

public ConstantFolding();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 100
7: putfield #2; //Field a:I
10: aload_0
11: sipush 200
14: putfield #3; //Field b:I
17: return

public final void baz();
Code:
0: sipush 300
3: istore_1
4: return

}

Here also, an sipush 300 is used.

For the second case (dead-code elimination), I used the following test program:

public class InlineTest {

private static final boolean debug = false;

private void baz() {
if(debug) {
String a = foo();
}
}

private String foo() {
return bar();
}

private String bar() {
return "abc";
}
}

which gives the following bytecode:

Compiled from "InlineTest.java"
public class InlineTest extends java.lang.Object{
private static final boolean debug;

public InlineTest();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

private void baz();
Code:
0: return

private java.lang.String foo();
Code:
0: aload_0
1: invokespecial #2; //Method bar:()Ljava/lang/String;
4: areturn

private java.lang.String bar();
Code:
0: ldc #3; //String abc
2: areturn

}

As you can see, the foo is not called at all in baz because the code inside the if block is effectively "dead".

Sun's (now Oracle's) HotSpot JVM combines interpretation of the bytecode as well as JIT compilation. When bytecode is presented to the JVM the code is initially interpreted, but the JVM will monitor the bytecode and pick out parts that are frequently executed. It coverts these parts into native code so that they will run faster. For piece of bytecode that are not used so frequently, this compilation is not done. This is just as well because compilation has some overhead. So it's really a question of tradeoff. If you decide to compile all bytecode to nativecode, then the code can have a very long start-up delay.

In addition to monitoring the bytecode, the JVM can also perform static analysis of the bytecode as it is interpreting and loading it to perform further optimization.

If you want to know the specific kinds of optimizations that the JVM performs, this page at Oracle is pretty helpful. It describes the performance techniques used in the HotSpot JVM.

Does Java has a compiler directive to inline a method?

The Java compiler does very few optimisations at compile time.

Instead the JIT does most of the optimisations at runtime based on how the applciation is actually used. It can inline methods, even up to two "virtual" methods.



Related Topics



Leave a reply



Submit