Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java8 slow compiling for interfaces with thousands of default methods with the same name

given the interfaces (which are very large and generated out of language definitions):

interface VisitorA {
   default void visit(ASTA1 node) {...}
   ...
   default void visit(ASTA2000 node) {...}
}

interface VisitorB extends VisitorA {
   default void visit(ASTB1 node) {...}
   ...
   default void visit(ASTB1000 node) {...}

   // due to language embedding all visit methods of VisitorA
   // must be overwritten
   @Override
   default void visit(ASTA1 node) {...}
   ...
   @Override
   default void visit(ASTA2000 node) {...}
}

interface VisitorC extends VisitorA {
   default void visit(ASTC1 node) {...}
   ...
   default void visit(ASTC1000 node) {...}

   // due to language embedding all visit methods of VisitorA
   // must be overwritten
   @Override
   default void visit(ASTA1 node) {...}
   ...
   @Override
   default void visit(ASTA2000 node) {...}
}

interface VisitorD extends VisitorB, VisitorC {
   default void visit(ASTD1 node) {...}
   ...
   default void visit(ASTD1000 node) {...}

   // due to language embedding all visit methods of VisitorA,
   // VisitorB, and VisitorC must be overwritten
   @Override
   default void visit(ASTA1 node) {...}
   ...
   @Override
   default void visit(ASTA2000 node) {...}

   @Override
   default void visit(ASTB1 node) {...}
   ...
   @Override
   default void visit(ASTB1000 node) {...}

   @Override
   default void visit(ASTC1 node) {...}
   ...
   @Override
   default void visit(ASTC1000 node) {...}
}

Now compiling the interface VisitorA (containing of about 2.000 overloaded methods) needs about 10s. Compiling the interfaces VisitorB and VisitorC needs each about 1.5 min. But when we try to compile the interface VisitorD, the Java 8 compiler needs about 7 minutes!

  • Has anybody an idea why it needs so much time to compile VisitorD?
  • Is it because of the inheritance of the default methods?
  • Or is it because of the diamond constellation, VisitorB as well as VisitorC extend both VisitorA and VisitorD extends VisitorB and VisitorC again?

We already tried around and the following solution helped a little bit:

 interface VisitorAPlain {
   void visit(ASTA1 node);
   ...
   void visit(ASTA2000 node);
}

interface VisitorA extends VisitorAPlain {
   ... // has same default methods as VisitorA above
}

interface VisitorBPlain extends VisitorAPlain {
   void visit(ASTB1 node);
   ...
   void visit(ASTB1000 node);
}

interface VisitorB extends VisitorBPlain {
   ... // has same default methods as VisitorB above
}

interface VisitorCPlain extends VisitorAPlain {
   void visit(ASTC1 node);
   ...
   void visit(ASTC1000 node);
}

interface VisitorC extends VisitorCPlain {
   ... // has same default methods as VisitorC above
}

interface VisitorD extends VisitorBPlain, VisitorCPlain {
   default void visit(ASTD1 node) {...}
   ...
   default void visit(ASTD1000 node) {...}

   // due to language embedding all visit methods of VisitorAPlain,
   // VisitorBPlain, and VisitorCPlain must be overwritten
   @Override
   default void visit(ASTA1 node) {...}
   ...
   default void visit(ASTA2000 node) {...}

   @Override
   default void visit(ASTB1 node) {...}
   ...
   default void visit(ASTB1000 node) {...}

   @Override
   default void visit(ASTC1 node) {...}
   ...
   default void visit(ASTC1000 node) {...}
}

And now the compilation time of the visitorD needs only about 2 minutes. But still this is a lot.

  • Has anybody an idea how to reduce the compilation time of VisitorD to a few seconds?
  • If we remove the two extends relation of VisitorD, extends VisitorBPlain, VisitorCPlain, then the compilation time of this interface needs about 15s - even though it has about 5.000 default methods. But we need the that VisitorD is compatible to VisitorB and VisitorC (either by direct extension or the indirect one with the intermediate Plain-interfaces) for casting reasons.

I also read the answers to the similar question: slow JDK8 compilation but there the problem seemed to be the with generic type inference: "There's a severe performance regression in Java 8 when it comes to overload resolution based on generic target typing."

So this is kind of different, if anybody would have a tip or a good explanation why it is so; I would be very thankful.

Thank you, Michael

like image 866
Michael von Wenckstern Avatar asked Aug 23 '16 22:08

Michael von Wenckstern


People also ask

What happens when a class implements two interfaces having the same default method name?

So, if the class already has the same method as an Interface, then the default method from the implemented Interface does not take effect. However, if two interfaces implement the same default method, then there is a conflict.

Can we have multiple default methods in interface?

Multiple Defaults With default functions in interfaces, there is a possibility that a class is implementing two interfaces with same default methods. The following code explains how this ambiguity can be resolved. First solution is to create an own method that overrides the default implementation.

Can we override default method of interface Java 8?

A default method cannot override a method from java.

Why do we need default method in Java 8 interfaces?

Default methods enable you to add new functionality to existing interfaces and ensure binary compatibility with code written for older versions of those interfaces.


1 Answers

The credit for this answer goes to @Brian Goetz.

I created a dummy test, where once all the visit methods were overwritten and overloaded, at the other time where the visitX methods got different names.

And the outcome was more amazing than I thought: When overloading and overwriting the visit methods, the compiler needed nearly 30 minutes! When I renamed the visit methods uniquely inside one visitor class, the compiler needed only 46 seconds.

Here is the source code for the dummy test: https://drive.google.com/open?id=0B6L6K365bELNUkVYMHZnZ0dGREk

And here are the screenshots for the compile time at my computer: VisitorN contains overloaded and overwritten visit methods. VisitorG contains the optimized visitX methods, which are only overwritten but not overloaded anymore. <code>VisitorN</code> contains overloaded and overwritten <code>visit</code> methods <code>VisitorG</code> contains the optimized <code>visitX</code> methods, which are only overwritten but not overloaded anymore

Using the "plain" approach with different visitX methods, then compiling Visitor_S and VisitorPlain_S needs only about 22 seconds (being twice as fast as the approach with overloading directly the default visitX methods). Visitor_S has default methods, but it extends VisitorPlain_S having no default methods. VisitorPlain_S extends other "plain" visitors without default methods. <code>Visitor_S</code> has <code>default</code> methods, but it extends <code>VisitorPlain_S</code> having no <code>default</code> methods. <code>VisitorPlain_S</code> extends other "plain" visitors without <code>default</code> methods.

But what I do still not understand -- just for my theoretical interest, is the fact with the bridge methods: In https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html bridge methods only occur doe to type erasing, but in the example we had no generics and so type erasing should not play a role at all. - Maybe anybody has a good explanation why it still maters.

like image 111
Michael von Wenckstern Avatar answered Sep 28 '22 15:09

Michael von Wenckstern