Can Java 8 default interface methods be inlined by the JIT compiler?
Asked Answered
M

2

7

I was analyzing the HotSpot logs for a benchmark I ran for a piece of code in JITWatch and noticed a lot of methods calls not being inlined due to "no static binding". These seem to only occur for calls to default interface methods.

My question is are default interface methods preventing the JIT compiler from inlining their calls?

interface A {
    default double a() {
        return Math.random();
    }
}

interface B extends A {
    default double b() {
        return a();
    }
}

class C implements B {
    public double c() {
        double c = 0;
        for (int i = 0; i < 1_000_000; ++i) {
            c += b();
        }
        return c;
    }

    public static void main(String[] args) {
        System.out.println(new C().c());
    }
}

Upon further inspection in JITWatch, it seems that this problem pertains to default interface methods calling other default interface methods. That would make more sense given the "no static binding" message.

Mairemaise answered 28/12, 2020 at 6:18 Comment(4)
Look here: github.com/openjdk/jdk/blob/master/src/hotspot/share/c1/… Maybe it can help you...Hallowell
JIT can inline default methods, and I see it actually does. If you are asking about a problem with a specific code, please provide MCVE,Diction
BTW, "no static binding" message is specific to C1 only.Diction
@Diction I've added an example.Mairemaise
N
2

Eugene's example shows that default methods can be inlined.

In fact, I think that the criteria for inlining should be the same as for any other non-static method.

  • The size of the code to be inlined must be smaller than a tunable threshold.
  • The method must not be overridden by a method in any (currently loaded) subclass of the class or interface.

In your example, I think that inlining should be possible, assuming that this is all of the code involved in the example.

However, there may be other limitations with / in the specific JIT that is being used here. For example, a default method calling another default method might be an edge case that is rare enough that it was deemed not worth supporting. Another possible explanation is that the C1 compiler doesn't do deep monomorphic dispatch analysis / optimization.

And the flip-side of this is that this could be premature optimization ... unless your performance profiling has identified a specific hotspot in your code where inlining could make a significant difference. Normally, the best strategy is to leave this to the compiler. If you micro-optimize your code to give optimal performance for a given Java version, there is a good chance that you will need to redo the work when you change to a newer version.

Newsboy answered 29/12, 2020 at 3:52 Comment(3)
I see. I do see that in most cases where the C1 compiler fails to inline default interface methods, the C2 compiler does come in later and inline them for being "hot". This is only apparent when I manually dug through the inlining reports for various methods in JITWatch instead of just scanning over its "Suggestions" window.Mairemaise
@HaoZhang C1 will always fail to inline a default method, as my answer has shown you, but I do not think you understood that, did you?Resolute
@Resolute There are still cases where C2 fails to inline them due to callee size, but I'm aware that is entirely configurable. Whether we will configure our JVM to increase that limit is up for discussion.Mairemaise
R
2

It is inlined. Here is an example:

public class DefaultInline {

    public static void main(String[] args) {
        System.out.println(callMe());
    }

    static int callMe(){
        A instance = new A(){};
        int x = 0;
        for (int i = 0; i < 1_000_000; ++i) {
            x += (int)instance.myRandom();
        }
        return x;
    }

    interface A {
        default double myRandom() {
            return Math.random();
        }
    }

}

Run it with:

java -XX:+UnlockDiagnosticVMOptions 
     -XX:+PrintInlining 
     -XX:CICompilerCount=2 
     DefaultInline.java

And see a line that has:

@ 20   zero.x.so.DefaultInline$A::myRandom (4 bytes)   inline

As to that "no static binding", it is present here, notice that this is in C1. Because calling the method myRandom compiles with invokeInterface (and you can look above at the types of the methods C1 will inline), C1 compiler will not inline it (as far as I understand the code), but C2 will.

Resolute answered 28/12, 2020 at 20:19 Comment(2)
Does this inlining occur when, say, interface A extends interface B and myRandom() calls a default method in interface B? See my original post for an example.Mairemaise
@HaoZhang but that is trivial to find out, no? Just do a minor refactor of this example and seeResolute
N
2

Eugene's example shows that default methods can be inlined.

In fact, I think that the criteria for inlining should be the same as for any other non-static method.

  • The size of the code to be inlined must be smaller than a tunable threshold.
  • The method must not be overridden by a method in any (currently loaded) subclass of the class or interface.

In your example, I think that inlining should be possible, assuming that this is all of the code involved in the example.

However, there may be other limitations with / in the specific JIT that is being used here. For example, a default method calling another default method might be an edge case that is rare enough that it was deemed not worth supporting. Another possible explanation is that the C1 compiler doesn't do deep monomorphic dispatch analysis / optimization.

And the flip-side of this is that this could be premature optimization ... unless your performance profiling has identified a specific hotspot in your code where inlining could make a significant difference. Normally, the best strategy is to leave this to the compiler. If you micro-optimize your code to give optimal performance for a given Java version, there is a good chance that you will need to redo the work when you change to a newer version.

Newsboy answered 29/12, 2020 at 3:52 Comment(3)
I see. I do see that in most cases where the C1 compiler fails to inline default interface methods, the C2 compiler does come in later and inline them for being "hot". This is only apparent when I manually dug through the inlining reports for various methods in JITWatch instead of just scanning over its "Suggestions" window.Mairemaise
@HaoZhang C1 will always fail to inline a default method, as my answer has shown you, but I do not think you understood that, did you?Resolute
@Resolute There are still cases where C2 fails to inline them due to callee size, but I'm aware that is entirely configurable. Whether we will configure our JVM to increase that limit is up for discussion.Mairemaise

© 2022 - 2024 — McMap. All rights reserved.