7
votes

Java8 Compilation lente pour les interfaces avec des milliers de méthodes par défaut avec le même nom

Compte tenu des interfaces (qui sont très volumineuses et générées de définitions de langue): xxx

compilent maintenant l'interface visitora (contenant d'environ 2.000 méthodes surchargées) ont besoin d'environ 10s. Compiler les interfaces visitorb et visitorc a besoin d'environ 1,5 min. Mais lorsque nous essayons de compiler l'interface Visitord, le compilateur Java 8 a besoin d'environ 7 minutes!

  • Quelqu'un a-t-il une idée pourquoi il a besoin tellement de temps pour compiler Visitord?
  • est-ce à cause de l'héritage des méthodes par défaut?
  • ou est-ce à cause de la constellation de Diamond, Visitorb et VisitorC étendre à la fois Visitora et Visitord s'étend à nouveau visitorb et visitorc?

    Nous avons déjà essayé et la solution suivante a aidé un peu: xxx

    et maintenant le temps de compilation du Visitord n'a besoin que d'environ 2 minutes. Mais c'est toujours beaucoup.

    • a quelqu'un une idée comment réduire le temps de compilation de Visitord en quelques secondes?
    • Si nous supprimons les deux étendes de la relation de Visitord, étend Visitorbplain, VisitorCplain , puis le temps de compilation de cette interface a besoin d'environ 15 ans - même s'il dispose d'environ 5 000 méthodes par défaut. Mais nous avons besoin que le Visitord soit compatible avec VisitorB et VisitorC (soit par extension directe, soit indirect avec les interfaces ordinaires intermédiaires) pour des raisons de casting.

      Je lis également les réponses à la question similaire: compilation lente JDK8 Mais là, le problème semblait être l'inférence de type générique: "Il existe une grave régression de performance dans Java 8 lorsqu'il s'agit de la résolution de la surcharge basée sur la typographie générique de la cible."

      Donc, c'est un peu différent, si quelqu'un aurait une pointe ou un bon explication pourquoi c'est le cas; Je serais très reconnaissant.

      Merci, Michael


8 commentaires

Désolé, je n'ai pas de réponse mais je suis curieux - Quelle est la taille de ces fichiers? J'ai un projet avec environ 1000 fichiers totalisant environ 150 000 lignes et, avec Maven, il prend un peu plus de 15 secondes pour compiler. Vous devez avoir des fichiers majeurs.


Ce fichier est très grand. Il a environ 270 kb grand. Je l'ai téléchargé, vous pouvez donc voir par vous-même: drive.google.com/open?id=0B6L6K365BELNBXFHZVP6MG55RU0


Dans notre constellation, les fichiers de visiteur générés ont environ 500 à 2 000 méthodes dans un fichier. Et comme dans l'exemple de lien ci-dessus, un visiteur de délégation étend principalement une ou deux autres visiteurs de délégation ayant également ca. 500 à 2.000 méthodes dans un fichier. Et ensuite, il existe plusieurs étapes d'extension: dans l'extension de langue générale (et aussi l'extension des visiteurs) est la suivante: Java s'étend commune, Montiarc étend Java, Montiarcbehavior s'étend Montiarc, Automaton s'étend commun, Automatonjava s'étend automaton et Java, Montiarcautomaton (le fichier téléchargé ) prolonge montiarcbehavior et automatejava


Ceci est causé par toutes les méthodes ayant le même nom. Donc, pour vérifier que le remplacement est correct (ne remplace pas accidentellement un pont), chaque méthode doit être vérifiée les unes contre l'autre, ce qui devient quadratique (ou pire). Avoir des milliers de méthodes portant le même nom dans une classe met beaucoup de stress sur la sélection / la vérification de la surcharge, c'est pourquoi vous voyez cela. (Notez que cela ne vient pas trop souvent dans le code écrit à la main, vient de générer du code.)


@BrianGOETZ Que voudriez-vous nous suggérer? Ne pas utiliser l'héritage dans ce cas et laisser le générateur copier toutes les méthodes d'une classe de base à la classe héritée; Ensuite, nous n'avons plus de méthodes écrasées - et il devrait devenir plus rapide, ou? Mais ce qui me surprend également, c'est que je pensais que le remplacement d'un pont ne doit être vérifié que si l'une de vos classes de base a des génériques; Mais toutes les classes de visiteurs sont générées et aucune d'entre elles ne contient de générosité - ni de la vérification faite de toute façon?


Nommez les méthodes visitast1 (AST1) . (C'est ce que la plupart des visiteurs font de toute façon.)


Mais que la double dispatching ne fonctionne plus. Ou comment l'avez-vous résolu?


Le accepter (visitor v) méthode dans ast1 délégués à v.visitas1 (this) .


3 Réponses :


2
votes

Le crédit pour cette réponse va à @brian Goetz.

J'ai créé un test factice, où toutes les méthodes ont été écrasées et surchargées, à l'autre moment où les méthodes visitex ont obtenu des noms différents.

Et le résultat était plus étonnant que je pensais: Lors de la surcharge et de l'écrasement des méthodes VISITES , le compilateur nécessitait presque 30 minutes ! Quand j'ai renommé les méthodes Visitez uniquement à l'intérieur d'une classe de visiteurs, le compilateur n'avait besoin que 46 secondes . .

Voici le code source du test factice: https://drive.google.com/open?id=0b6l6k365belnukvymhznz0dgrek

et voici les captures d'écran de la compilation de mon ordinateur: Visitorn Contient des méthodes surchargées et écrasées . VISITORG contient les méthodes optimisées visitex , qui ne sont plus écrasées mais non surchargées. <code> visitorn </ code> contient surchargé et écrasé <code> visitez </ code> méthodes <code> visitorg </ code> contient les méthodes optimisées <code> visitex </ code> uniquement écrasées mais non surchargées

Utilisation de l'approche "ordinaire" avec des méthodes différentes visitex , puis compilation visitor_s et visitorplain_s ne nécessite que 22 secondes (étant deux fois plus rapide que l'approche avec la surcharge directement sur les méthodes Par défaut par défaut). Visitor_s a Par défaut par défaut, mais il étend visitorplain_s sans Par défaut méthodes. VisitorPlain_s étend d'autres "ordinateurs" sans Par défaut . Visitor_s a par défaut , mais il étend visitorplain_s sans par défaut méthodes. visitorplain_s prolonge d'autres visiteurs "ordinaires" sans Par défaut Méthodes. ">

Mais ce que je ne comprends toujours pas - juste pour mon intérêt théorique, c'est le fait avec les méthodes de pont : Dans https://docs.oracle.com/javase/tatutorial/java /Generics/briidgemethods.html Les méthodes de pont ne se produisent qu'à une inastation de type, mais dans l'exemple, nous n'avions aucun générique et que l'effacement de type ne devrait pas jouer un rôle du tout. - Peut-être que quiconque a une bonne explication pourquoi elle noue toujours.


0 commentaires

1
votes

Après une réunion supplémentaire juste pour ce problème, nous avons compris les limitations suivantes de la première réponse:

La première réponse fonctionne très bien pour les visiteurs "statiques", comme ils sont utilisés dans ANTLR, car il n'y a pas de avoir des interfaces de langue, et donc la méthode sait exactement les enfants asttypes . À Monticore, nous pouvons définir un élément de grammaire d'interface qui sera expliqué ici maintenant: xxx

le visiteur pour Montiarcast ne sait pas exactement ce que < code> accepter la méthode doit être invoqué, car vous ne savez pas si vous devriez appeler portatrast # Accepter ou même la méthode non connue Etat Accepter , ce qui sera être introduit ultérieurement en raison de l'extension de la grammaire. C'est pourquoi nous utilisons "Double Dispatching", mais donc les méthodes visitent doivent avoir le même nom (puisque nous ne savions pas la méthode visiteux (noeud de statue) qui n'est pas Là quand nous générons le visiteur pour le Montiarc grammaire.

Nous avons pensé à générer Visitex méthode et déléguer à cette méthode à partir du général Méthode à l'aide d'un grand instance de -IF-CASCADE. Mais cela nécessiterait d'ajouter des énoncés si sur le visiter (noeud montiarcast) Après le déploiement de notre fichier jar de la grammaire Montiarc , et cela détruirait notre module.

Nous allons essayer d'analyser le problème plus loin et je vous tiendrai à la hauteur -Date si nous avons trouvé une nouvelle méthologie comment générer de grands visiteurs dynamiques.


0 commentaires

0
votes

Nous avons compris comment résoudre le problème pour nous: Nous avons eu un bug dans le générateur car la méthode héritée surchargée avait Le même corps de méthode que celui hérité de.

Cela signifierait pour nous que nous avons deux méthodes comment le résoudre: p>

  • (a) ne génère pas les méthodes que nous avons héritées plus li>
  • (b) génère toutes les méthodes, mais supprimer l'héritage de l'interface li> ul>

    La chose intéressante est que (a) strong> a besoin de plus de temps de compilation que (b) b) p>

    J'ai fait une expérience sur Mon Mac Pour représenter les résultats que nous avons trouvés lors de notre processus de fixation, que vous pouvez télécharger à: https://drive.google.com/open?id=0b6l6k365belnwdroetf4rxjsafk P >

    Je décris que les fichiers de base de l'expérience ici et les résultats. Peut-être que quiconque le trouve utile. P>

    la version 1 est (b) et ressemble à: strong> p>

    DélégateurVisitora.java P>

    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
    


0 commentaires