46 votes

Que puis-je faire pour mon scala code afin de compiler plus vite?

J'ai un grand scala base de code. (https://opensource.ncsa.illinois.edu/confluence/display/DFDL/Daffodil%3A+Ouvrir+Source+DFDL)

C'est comme 70K lignes de code scala. Nous sommes sur scala 2.11.7

Le développement est devenu difficile car la compilation - l'édition-compilation-test-debug cycle est trop long pour que de petits changements.

Différentiels recompiler fois peut-être une minute, et c'est sans optimisation activée. Parfois plus longtemps. Et c'est de ne pas avoir édité de très nombreux changements dans les fichiers. Parfois, un très petit changement provoque un énorme recompilation.

Donc ma question: Que puis-je faire en sorte d'organiser le code, qui permettront d'améliorer le temps de compilation?

E. g., la décomposition du code dans des fichiers plus petits? Cela aiderait-il?

E. g., plus petites bibliothèques?

E. g., éviter l'utilisation de l'implicites? (nous avons très peu)

E. g., éviter l'utilisation de traits? (nous avons des tonnes)

E. g., éviter beaucoup d'importations? (nous avons des tonnes - paquet limites sont assez chaotique en ce moment)

Ou est-il vraiment rien que je puisse faire à ce sujet?

Je me sens comme cette très longue compilation est en quelque sorte en raison de certaines immense quantité de recompiler à cause des dépendances, et je pense à la façon de réduire les fausses dépendances....mais c'est juste une théorie

J'espère que quelqu'un d'autre peut faire la lumière sur quelque chose que nous pourrions faire qui permettrait d'améliorer la vitesse de compilation pour des changements progressifs.

6voto

khakishoiab Points 1787

Voici les phases du compilateur scala, avec légèrement modifié des versions de leurs commentaires à partir du code source. Notez que cette compilateur est inhabituel en étant fortement orientée vers la vérification de type et à des transformations qui sont plus comme des desugarings. D'autres compilateurs inclure beaucoup de code pour: optimisation, l'allocation de registres, et de la traduction à l'IR.

Certains haut-niveau des points: Il y a beaucoup d'arbre de réécriture. Chaque phase a tendance à lire dans un arbre à partir de la phase précédente et de le transformer en un nouvel arbre. Symboles, à revanche, demeurent significatifs tout au long de la vie du compilateur. Donc les arbres de tenir des pointeurs vers des symboles, et non pas vice versa. Au lieu de la réécriture de symboles, de nouvelles informations sont attachées à eux comme les phases les progrès accomplis.

Voici la liste des phases de Mondial:

 analyzer.namerFactory: SubComponent,
    analyzer.typerFactory: SubComponent,
    superAccessors,  // add super accessors
    pickler,         // serializes symbol tables
    refchecks,       // perform reference and override checking,
translate nested objects
    liftcode,        // generate reified trees
    uncurry,         // uncurry, translate function values to anonymous
classes
    tailCalls,       // replace tail calls by jumps
    explicitOuter,   // replace C.this by explicit outer pointers,
eliminate pattern matching
    erasure,         // erase generic types to Java 1.4 types, add
interfaces for traits
    lambdaLift,      // move nested functions to top level
    constructors,    // move field definitions into constructors
    flatten,         // get rid of inner classes
    mixer,           // do mixin composition
    cleanup,         // some platform-specific cleanups
    genicode,        // generate portable intermediate code
    inliner,         // optimization: do inlining
    inlineExceptionHandlers, // optimization: inline exception handlers
    closureElimination, // optimization: get rid of uncalled closures
    deadCode,           // optimization: get rid of dead cpde
    if (forMSIL) genMSIL else genJVM, // generate .class files

certains contourner avec le compilateur scala

Ainsi, scala compilateur doit faire beaucoup plus de travail que le compilateur Java, cependant, en particulier, il y a des choses qui rend le compilateur Scala considérablement plus lent, qui comprennent

  • Implicite de la résolution. Implicite de la résolution (c'est à dire scalac, essayant de trouver une valeur implicite lorsque vous faites un implicite du doctype) bulles-dessus de chaque parent dans la déclaration, ce temps de recherche peut être énorme (surtout si vous faites référence à la même implicite variable de nombreuses fois, et elle le déclare dans une bibliothèque tout le chemin vers le bas de votre dépendance de la chaîne). Le temps de compilation est encore pire quand vous prenez en compte implicite trait résolution et type de classes, qui est largement utilisé par les bibliothèques comme scalaz et informe. Aussi à l'aide d'un grand nombre de classes anonymes (c'est à dire des lambdas, des blocs, des fonctions anonymes).Les Macros évidemment ajouter au moment de la compilation.

    Un très bel article de Martin Odersky

    Plus le Java et Scala compilateurs convertissez le code source en bytecode JVM et n'ont que très peu d'optimisation.Sur la plupart des modernes Jvm, une fois que le programme bytecode est exécuté, il est converti en code machine pour l'architecture d'ordinateur sur lequel il est exécuté. Ceci est appelé le juste-à-temps de compilation. Le niveau de l'optimisation du code est, toutefois, bas avec juste-à-temps de compilation, car il se doit d'être rapide. Pour éviter de recompiler, appelé HotSpot compilateur optimise les parties du code qui sont exécutés fréquemment.

    Un programme peut avoir des performances différentes à chaque fois qu'il est exécuté. L'exécution de la même morceau de code (par exemple, une méthode) plusieurs fois dans la même JVM exemple, pourrait donner de très différent des résultats de performance en fonction de si le code a été optimisé entre les pistes. En outre, de mesurer le temps d'exécution d'un morceau de code peut inclure le temps pendant lequel le compilateur JIT lui-même a été l'exécution de l'optimisation, et donc de donner des résultats incohérents.

    Une cause commune d'une détérioration des performances est également boxing et unboxing qui se passe implicitement lors du passage d'un type primitif comme argument à une méthode générique et aussi fréquentes GC.

    Il existe plusieurs approches pour éviter les effets ci-dessus au cours de la mesure,comme Il doit être exécuté à l'aide du serveur de la version de la JVM HotSpot, qui n'des optimisations agressives.Visualvm est un excellent choix pour le profilage de la JVM de l'application. C'est un outil visuel qui permet l'intégration de plusieurs ligne de commande JDK outils et léger fonctionnalités de profiling.Cependant scala abstracions sont très complexes et, malheureusement, VisualVM ne prend pas encore en charge cette.l'analyse des mécanismes qui prenait beaucoup de temps à traiter comme de la cause à l'aide de beaucoup d' exists et forall qui sont des méthodes de la Scala de collections qui prennent des prédicats,les prédicats de la FOL et peut donc passer toute la séquence, l'optimisation des performances.

    Aussi les modules cohisive et moins dépendante est une solution viable.L'esprit que le code intermédiaire gen est parfois dépend de la machine et de divers architechures donner des résultats variés.

    Une Alternative:Typesafe a publié le Zinc qui sépare la rapide différentiels compilateur de sbt et permet à l'maven/d'autres outils de construction de l'utiliser. Ainsi, à l'aide de Zinc avec la scala plugin maven a fait de compiler beaucoup plus vite.

    Un problème simple: étant Donné une liste de nombres entiers, de supprimer le plus grand. La commande n'est pas nécessaire.

Ci-dessous est la version de la solution (Une moyenne je suppose).

def removeMaxCool(xs: List[Int]) = {
  val maxIndex = xs.indexOf(xs.max);
  xs.take(maxIndex) ::: xs.drop(maxIndex+1)
}

C'est Scala idiomatiques, concis, et en utilise qu'une belle liste de fonctions. Il est également très inefficace. Il parcourt la liste d'au moins 3 ou 4 fois.

Maintenant, considérons ce , Java-comme solution. C'est aussi ce raisonnable développeur Java (ou Scala novice) serait d'écrire.

def removeMaxFast(xs: List[Int]) = {
    var res = ArrayBuffer[Int]()
    var max = xs.head
    var first = true;   
    for (x <- xs) {
        if (first) {
            first = false;
        } else {
            if (x > max) {
                res.append(max)
                max = x
            } else {
                res.append(x)
            }
        }
    }
    res.toList
}

Totalement non-Scala idiomatiques, non fonctionnels, non-concise, mais elle est très efficace. Il parcourt la liste qu'une seule fois!

Donc compromis devrait également être une priorité et parfois vous pouvez avoir des choses à travailler comme développeur java si personne d'autre.

4voto

Aleksey Izmailov Points 4514

Quelques idées qui pourraient aider - dépend de votre cas et de style de développement:

  • Utilisation compilation incrémentielle ~compile dans SBT ou fourni par votre IDE.
  • Utilisation sbt-revolver et peut-être JRebel pour recharger votre application plus rapidement. Mieux adapté pour les applications web.
  • Utilisation TDD - plutôt que de l'exécution et de débogage de l'ensemble de l'application écrire des tests et n'exécutez ces.
  • Répartir votre projet dans les bibliothèques/Bocaux. Les utiliser comme des dépendances via votre outil de construction: SBT/Maven/etc. Ou une variante de ce côté...
  • Pause votre projet en sous-projets (SBT). Compiler séparément ce qui est nécessaire ou à la racine du projet si vous avez besoin de tout. Compilation incrémentielle est toujours disponible.
  • Répartir votre projet de microservices.
  • Attendre Dotty pour résoudre votre problème à un certain degré.
  • Si tout échoue, n'utilisez pas avancé Scala caractéristiques qui font de compilation plus lent: implicites, la métaprogrammation, etc.
  • N'oubliez pas de vérifier que vous allouer assez de mémoire et de CPU pour votre compilateur Scala. Je n'ai pas essayé, mais peut-être que vous pouvez utiliser RAM disque à la place du HDD pour vos sources et compiler des artefacts (facile sur Linux).

1voto

firephil Points 163

Vous touchez l'un des principaux problèmes de conception orientée objet (en plus de l'ingénierie), à mon avis, vous devez aplatir votre classe-objet-trait de la hiérarchie et de réduire le avec des dépendances entre les classes. Paquets de freinage pour différents fichiers jar et de les utiliser comme des mini-bibliothèques qui sont "gelés" et de se concentrer sur le nouveau code.

Vérifiez quelques vidéos aussi de Brian Va, qui fait une affaire contre OO sur-ingénierie

j'.e https://www.youtube.com/watch?v=IRTfhkiAqPw (vous pouvez prendre les bons points)

Je ne suis pas d'accord avec lui à 100%, mais il fait une bonne affaire, contre le génie.

Espérons que cela aide.

0voto

jlncrnt Points 36

Vous pouvez essayer d'utiliser le Fast Compilateur Scala.

0voto

airudah Points 972

Des réserves mineures du code des améliorations comme un (e.g @tailrec annotations), en fonction de comment vous vous sentez courageux, vous pouvez aussi jouer avec Dotty qui revendique la plus rapide temps de compilation entre autres choses.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X