Question

Dans Scala, y a-t-il un impact CPU ou mémoire significatif sur l'utilisation de conversions de type implicite pour augmenter la fonctionnalité d'une classe par rapport à d'autres choix de mise en œuvre possibles?

Par exemple, considérons une fonction de manipulation de chaîne idiote. Cette implémentation utilise la concaténation des chaînes:

object Funky {
  def main(args: Array[String]) {
    args foreach(arg => println("Funky " + arg))
  }
}

Cette implémentation masque la concaténation derrière une méthode de membre en utilisant une conversion de type implicite:

class FunkyString(str: String) {
  def funkify() = "Funky " + str
}

object ImplicitFunky {
  implicit def asFunkyString(str: String) = new FunkyString(str)

  def main(args: Array[String]) {
    args foreach(arg => println(arg.funkify()))
  }
}

Les deux font la même chose:

scala> Funky.main(Array("Cold Medina", "Town", "Drummer"))        
Funky Cold Medina
Funky Town
Funky Drummer

scala> ImplicitFunky.main(Array("Cold Medina", "Town", "Drummer"))
Funky Cold Medina
Funky Town
Funky Drummer

Y a-t-il une différence de performance? Quelques considérations spécifiques:

Scala inline les appels implicites à la méthode asfunkystring?

Scala crée-t-il réellement un nouvel objet Funkystring en wrapper pour chaque arg, ou peut-il optimiser les allocations d'objets supplémentaires?

Supposons que Funkystring avait 3 méthodes différentes (FunKify1, Funkify2 et Funkify3), et le corps de Forach a appelé chacun successivement:

println(arg.funkify1())
println(arg.funkify2())
println(arg.funkify3())

Scala répéterait-il la conversion 3 fois, ou optimiserait-il les conversions redondantes et le ferait-il une fois pour chaque itération de boucle?

Supposons plutôt que je capture explicitement la conversion dans une autre variable, comme ceci:

val fs = asFunkyString(arg)
println(fs.funkify1())
println(fs.funkify2())
println(fs.funkify3())

Cela change-t-il la situation?

En termes pratiques, une large utilisation des conversions implicites est-elle un problème de performance potentiel, ou est-ce généralement inoffensif?

Était-ce utile?

La solution

J'ai essayé de configurer un microbenchmarché en utilisant l'excellent Scala-Benchmark-Template.

Il est très difficile d'écrire une référence significative (non optimisée par le JIT) qui teste uniquement les conversions implicites, j'ai donc dû ajouter un peu de frais généraux.

Voici le code:

class FunkyBench extends SimpleScalaBenchmark {
  val N = 10000
  def timeDirect( reps: Int ) = repeat(reps) {
    var strs = List[String]()
    var s = "a"
    for( i <- 0 until N ) {
      s += "a"
      strs ::= "Funky " + s 
    }
    strs
  }
  def timeImplicit( reps: Int ) = repeat(reps) {
    import Funky._
    var strs = List[String]()
    var s = "a"
    for( i <- 0 until N ) {
      s += "a"
      strs ::= s.funkify
    }
    strs
  }
}

Et voici les résultats:

[info] benchmark  ms linear runtime
[info]    Direct 308 =============================
[info]  Implicit 309 ==============================

Ma conclusion: Dans tout morceau de code non trivial, l'impact des conversions implicites (création d'objets) n'est pas mesurable.

ÉDITER: J'ai utilisé Scala 2.9.0 et Java 1.6.0_24 (en mode serveur)

Autres conseils

Jvm Peut optimiser les allocations d'objets supplémentaires, si elle détecte, ce serait digne.

Ceci est important, car si vous insérez des choses, vous vous retrouvez avec des méthodes plus importantes, ce qui pourrait entraîner des problèmes de performance avec le cache ou même réduire les risques de JVM appliquant d'autres optimisations.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top