Quel est l'impact sur les performances de l'utilisation du modèle de classe de type à Scala
-
27-10-2019 - |
Question
Je suis actuellement en utilisant largement le modèle de classe de type pour être des parties pertinentes performances de mon code. J'ai fait au moins deux sources potentielles d'inefficacité.
-
Les paramètres implicites sont transmis le long des appels de message. Je ne sais pas si cela se produit vraiment. Peut-être scalac peut simplement insérer les paramètres implicites où ils sont utilisés et les retirer de la signature de la méthode. Ceci est probablement pas possible dans le cas où vous insérez les paramètres implicites manuellement, car ils pourraient être résolus à l'exécution uniquement. Quelles optimisations ne s'appliquent en ce qui concerne le passage des paramètres implicites
-
Si l'instance de classe de type est fourni par un
def
(contrairement à unval
), l'objet doit être recréée à chaque invocation d'une « méthode de type classé ». Ce problème peut être par la machine virtuelle Java adressé, ce qui pourrait optimiser la création d'objets loin. Ce problème peut aussi être par scalac par adressé réutilisant ces objets. Quelles optimisations ne sont applicables en ce qui concerne la création d'objets de paramètres implicites?
Et bien sûr, il pourrait y avoir d'autres sources d'inefficacité lors de l'application du modèle de classe de type. S'il vous plaît me dire à leur sujet.
La solution
Si vous vous souciez vraiment à l'écriture de code ultra-haute performance (et vous pouvez que vous faites, mais être très mauvais à ce sujet), puis classes de types vont causer des la douleur pour les raisons suivantes:
- De nombreux appels supplémentaires méthode virtuelle
- la boxe probable des primitives (par exemple si vous utilisez des classes de types de scalaz pour monoids etc)
- créations d'objets via
def
qui sont nécessaires parce que les fonctions ne peuvent pas être paramétrés - créations d'objets pour accéder aux méthodes « souteneur »
Lors de l'exécution, la machine virtuelle Java peut optimiser quelques-unes des créations erronées loin (par exemple la création d'un MA
simplement appel <*>
), mais scalac
ne le fait pas grand-chose à l'aide. Vous pouvez voir cette trivialement en compilant un code qui utilise et en utilisant -Xprint:icode
classes de types comme argument.
Voici un exemple:
import scalaz._; import Scalaz._
object TC {
def main(args: Array[String]) {
println((args(0).parseInt.liftFailNel |@| args(1).parseInt.liftFailNel)(_ |+| _))
}
}
Et voici le icode:
final object TC extends java.lang.Object with ScalaObject {
def main(args: Array[java.lang.String]): Unit = scala.this.Predef.println(scalaz.this.Scalaz.ValidationMA(scalaz.this.Scalaz.StringTo(args.apply(0)).parseInt().liftFailNel()).|@|(scalaz.this.Scalaz.StringTo(args.apply(1)).parseInt().liftFailNel()).apply({
(new anonymous class TC$$anonfun$main$1(): Function2)
}, scalaz.this.Functor.ValidationFunctor(), scalaz.this.Apply.ValidationApply(scalaz.this.Semigroup.NonEmptyListSemigroup())));
def this(): object TC = {
TC.super.this();
()
}
};
@SerialVersionUID(0) final <synthetic> class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2 extends scala.runtime.AbstractFunction0 with Serializable {
final def apply(): Int = TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.this.v1$1;
final <bridge> def apply(): java.lang.Object = scala.Int.box(TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.this.apply());
<synthetic> <paramaccessor> private[this] val v1$1: Int = _;
def this($outer: anonymous class TC$$anonfun$main$1, v1$1: Int): anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2 = {
TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.this.v1$1 = v1$1;
TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.super.this();
()
}
};
@SerialVersionUID(0) final <synthetic> class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1 extends scala.runtime.AbstractFunction0$mcI$sp with Serializable {
final def apply(): Int = TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.apply$mcI$sp();
<specialized> def apply$mcI$sp(): Int = TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.v2$1;
final <bridge> def apply(): java.lang.Object = scala.Int.box(TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.apply());
<synthetic> <paramaccessor> private[this] val v2$1: Int = _;
def this($outer: anonymous class TC$$anonfun$main$1, v2$1: Int): anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1 = {
TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.v2$1 = v2$1;
TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.super.this();
()
}
};
@SerialVersionUID(0) final <synthetic> class TC$$anonfun$main$1 extends scala.runtime.AbstractFunction2$mcIII$sp with Serializable {
final def apply(x$1: Int, x$2: Int): Int = TC$$anonfun$main$1.this.apply$mcIII$sp(x$1, x$2);
<specialized> def apply$mcIII$sp(v1$1: Int, v2$1: Int): Int = scala.Int.unbox(scalaz.this.Scalaz.mkIdentity({
(new anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2(TC$$anonfun$main$1.this, v1$1): Function0)
}).|+|({
(new anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1(TC$$anonfun$main$1.this, v2$1): Function0)
}, scalaz.this.Semigroup.IntSemigroup()));
final <bridge> def apply(v1: java.lang.Object, v2: java.lang.Object): java.lang.Object = scala.Int.box(TC$$anonfun$main$1.this.apply(scala.Int.unbox(v1), scala.Int.unbox(v2)));
def this(): anonymous class TC$$anonfun$main$1 = {
TC$$anonfun$main$1.super.this();
()
}
}
}
Vous pouvez voir qu'il ya une énorme quantité de création d'un objet qui se passe ici