首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为具有数千个相同名称的默认方法的接口进行Java8缓慢编译

为具有数千个相同名称的默认方法的接口进行Java8缓慢编译
EN

Stack Overflow用户
提问于 2016-08-23 22:08:34
回答 3查看 414关注 0票数 7

考虑到接口(这些接口非常大,由语言定义生成):

代码语言:javascript
复制
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) {...}
}

现在编译接口VisitorA (包含大约2.000个重载方法)需要大约10s。编译接口VisitorB和VisitorC分别需要大约1.5分钟。但是当我们试图编译接口VisitorD时,Java8编译器需要大约7分钟!

  • 有人知道为什么需要这么多时间来编译VisitorD吗?
  • 这是因为默认方法的继承吗?
  • 还是因为钻石星座,VisitorB和VisitorC同时扩展了VisitorA,VisitorD又扩展了VisitorB和VisitorC?

我们已经尝试过了,下面的解决方案有一点帮助:

代码语言:javascript
复制
 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) {...}
}

和现在visitorD的编译时间只需要大约2分钟。,但这仍然是很多。

  • 有没有人知道如何将VisitorD的编译时间缩短到几秒钟?
  • 如果我们删除VisitorD和extends VisitorBPlain, VisitorCPlain的两个扩展关系,那么这个接口的编译时间需要15s --尽管它有大约5.000个默认方法。但出于浇铸的原因,我们需要VisitorD与VisitorB和VisitorC兼容(无论是直接扩展还是间接扩展,也可以是中间平面接口)。

我还阅读了类似问题的答案:slow JDK8 compilation,但问题似乎是泛型类型推断:“在Java8中,在基于泛型目标类型的重载解析方面存在严重的性能倒退。”

所以这是不一样的,如果任何人有一个提示或一个很好的解释为什么是这样的,我会非常感谢。

谢谢你,迈克尔

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-03-09 19:47:01

我们想出了如何解决这个问题的方法:我们在生成器中有一个错误,因为重载继承方法与继承的方法体相同。

这对我们来说意味着我们有两种方法来解决这个问题:

  • (a)不再生成我们继承的方法
  • (b)生成所有方法,但删除接口继承

有趣的是,(a)(b)需要更多的编译时间。

我在Mac上做了一个实验来表示我们在修复过程中发现的结果,您可以从:https://drive.google.com/open?id=0B6L6K365bELNWDRoeTF4RXJsaFk下载。

我只是在这里描述实验的基本文件和结果。也许有人觉得它有用。

版本1是(b),看起来像:

DelegatorVisitorA.java

代码语言:javascript
复制
interface DelegatorVisitorA extends VisitorA {
  VisitorA getVisitorA();  

  default void visit(AST_A1 node) {
    getVisitorA().visit(node);
  }
  ...
  default void visit(AST_A49 node) {
    getVisitorA().visit(node);
  }
}

DelegatorVisitorB.java

代码语言:javascript
复制
interface DelegatorVisitorB extends VisitorB {
  VisitorA getVisitorA();  
  default void visit(AST_A1 node) {
    getVisitorA().visit(node);
  }
  ...
  default void visit(AST_A49 node) {
    getVisitorA().visit(node);
  }
  VisitorB getVisitorB();  

  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
}

DelegatorVisitorC.java

代码语言:javascript
复制
interface DelegatorVisitorC extends VisitorC {
  VisitorA getVisitorA();
  default void visit(AST_A1 node) {
    getVisitorA().visit(node);
  }
  ...
  default void visit(AST_A49 node) {
    getVisitorA().visit(node);
  }
  VisitorB getVisitorB();  
  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
  VisitorC getVisitorC();  
  default void visit(AST_C1 node) {
    getVisitorC().visit(node);
  }
  ...
  default void visit(AST_C49 node) {
    getVisitorC().visit(node);
  }
}

版本2是(a),看起来像:

DelegatorVisitorA.java与版本1相同

DelegatorVisitorB.java

代码语言:javascript
复制
interface DelegatorVisitorB extends VisitorB , DelegatorVisitorA{
  VisitorB getVisitorB();
  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
}

DelegatorVisitorC.java

代码语言:javascript
复制
interface DelegatorVisitorC extends VisitorC , DelegatorVisitorB{
  VisitorB getVisitorB();
  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
}

第3版(我们有一个中间步骤,但也错了)看上去像:

DelegatorVisitorA.java与版本1相同

DelegatorVisitorB.java

代码语言:javascript
复制
interface DelegatorVisitorB extends VisitorB , DelegatorVisitorA{
  VisitorB getVisitorB();
  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
}

DelegatorVisitorC.java

代码语言:javascript
复制
interface DelegatorVisitorC extends VisitorC , DelegatorVisitorA, DelegatorVisitorB{
  VisitorB getVisitorB();
  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
}

版本4(导致这篇文章的旧版本)如下所示:

DelegatorVisitorA.java与版本1相同

DelegatorVisitorB.java

代码语言:javascript
复制
interface DelegatorVisitorB extends VisitorB , DelegatorVisitorA{
  VisitorA getVisitorA();  
  default void visit(AST_A1 node) {
    getVisitorA().visit(node);
  }
  ...
  default void visit(AST_A49 node) {
    getVisitorA().visit(node);
  }
  VisitorB getVisitorB();  

  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
}

DelegatorVisitorC.java

代码语言:javascript
复制
interface DelegatorVisitorC extends VisitorB , DelegatorVisitorA, DelegatorVisitorB{
  VisitorA getVisitorA();
  default void visit(AST_A1 node) {
    getVisitorA().visit(node);
  }
  ...
  default void visit(AST_A49 node) {
    getVisitorA().visit(node);
  }
  VisitorB getVisitorB();  
  default void visit(AST_B1 node) {
    getVisitorB().visit(node);
  }
  ...
  default void visit(AST_B49 node) {
    getVisitorB().visit(node);
  }
  VisitorC getVisitorC();  
  default void visit(AST_C1 node) {
    getVisitorC().visit(node);
  }
  ...
  default void visit(AST_C49 node) {
    getVisitorC().visit(node);
  }
}

这里我只展示了不同版本的DelegatorVisitorA.java、DelegatorVisitorB.java和DelegatorVisitorC.java。其他委托访问DelegatorVisitorD.java到DelegatorVisitorI.java的人遵循相同的模式。(DelegatorVisitorI属于扩展H语言的I语言,H语言有DelegatorVisitorH,H语言是扩展语言G,等等。)

如前所述,编译在四个不同版本中生成的DelegatorVisitorI.java的结果需要很长时间:

结果是:

代码语言:javascript
复制
Version 1:
103-240:srcV1 michael$ time javac DelegatorVisitorI.java

real    0m1.859s
user    0m5.023s
sys 0m0.175s



Version 2:
103-240:srcV2 michael$ time javac DelegatorVisitorI.java

real    0m3.364s
user    0m7.713s
sys 0m0.342s



Version 3:
103-240:srcV3 michael$ time javac DelegatorVisitorI.java

real    2m58.009s
user    2m56.787s
sys 0m1.718s



Version 4:
103-240:srcV4 michael$ time javac DelegatorVisitorI.java

real    14m14.923s
user    14m3.738s
sys 0m5.141s

所有四个不同版本的Java文件都有相同的行为,但是由于代码重复,编译过程需要更长的时间。

同样有趣的是,如果您复制方法并且不使用任何继承,那么编译是最快的,甚至文件在继承链非常长之后也会变得更大。

(我个人无法理解版本2和版本3之间的巨大时间差,可能是javac编译器分析过程中的一个错误。)

票数 0
EN

Stack Overflow用户

发布于 2016-08-25 22:50:34

这个答案的功劳是“Brian”。

我创建了一个虚拟测试,一旦所有visit方法都被覆盖和重载,那么visitX方法就会有不同的名称。

结果比我想象的更令人惊讶:当重载和覆盖visit方法时,编译器几乎需要30分钟!当我在一个访问者类中唯一地重命名visit方法时,编译器只需要46秒

下面是虚拟测试的源代码:https://drive.google.com/open?id=0B6L6K365bELNUkVYMHZnZ0dGREk

下面是在我的计算机上编译时的截图:VisitorN包含重载和覆盖的visit方法。VisitorG包含优化的visitX方法,这些方法只被覆盖而不再重载。

使用不同visitX方法的“普通”方法,那么编译Visitor_SVisitorPlain_S只需要大约22秒(是直接重载default visitX方法的方法的两倍)。Visitor_Sdefault方法,但它扩展了没有default方法的VisitorPlain_SVisitorPlain_S扩展了没有default方法的其他“普通”访问者。

但我仍然不明白--只是为了我的理论兴趣,是关于桥方法的事实:在https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html桥中,方法只发生类型擦除,但是在示例中,我们没有泛型,所以类型擦除应该完全不起作用。-也许任何人都能很好地解释为什么它仍然重要。

票数 2
EN

Stack Overflow用户

发布于 2016-08-26 11:00:17

在就这个问题举行了一次额外的会议之后,我们发现了第一个答案的以下局限性:

第一个答案对于“静态”访问者非常有用,因为他们在ANTLR中使用,因为在那里您没有语言接口,所以visit方法确切地知道子ASTTypes。在MontiCore中,我们可以定义一个接口语法元素,下面将在这里解释:

代码语言:javascript
复制
grammar MontiArc {
  MontiArc = "component" Name "{" ArcElement* "}";
  interface ArcElement;
  Port implements ArcElement = "port" ... ;
}

grammar MontiArcAutomaton extends MontiArc {
  Automaton implements ArcElement = State | Transition;
  State = "state" ... ;
  Transition = ... "->" ...;
}

MontiArcAST的访问者不知道应该调用哪个accept方法,因为您不知道是否应该调用PortAST#accept,甚至不知道方法State#accept,因为语法扩展将在稍后引入该方法。这就是为什么我们使用“双重分派”,但因此visit方法必须具有相同的名称(因为我们无法知道方法visitState(StateAST node),当我们为MontiArc语法生成访问者时,这个方法不存在。

我们考虑生成visitX方法,并使用大型instanceof-if级联从一般的instanceof方法委托给该方法。但这将需要在部署语法if的jar文件之后向visit(MontiArcAST node)添加额外的MontiArc语句,这将破坏我们的调制解调器。

我们将尝试进一步分析这个问题,如果我们找到了一种新的方法,如何产生大的动态访问者,我将让您了解最新的情况。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39111439

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档