29 votes

Quel est l'impact sur les performances des conversions implicites de types en Scala ?

En Scala, l'utilisation de conversions de types implicites pour augmenter la fonctionnalité d'une classe a-t-elle un impact significatif sur le processeur ou la mémoire par rapport à d'autres choix d'implémentation possibles ?

Prenons l'exemple d'une fonction idiote de manipulation des chaînes de caractères. Cette implémentation utilise la concaténation de chaînes de caractères :

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

Cette implémentation cache la concaténation derrière une méthode 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 met-il en ligne les appels implicites à la méthode asFunkyString ?

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

Supposons que FunkyString possède 3 méthodes différentes (funkify1, funkify2, et funkify3), et que le corps de foreach appelle chacune d'entre elles 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 ne la ferait-il qu'une fois pour chaque itération de la 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, l'utilisation généralisée des conversions implicites peut-elle poser un problème de performance ou est-elle généralement inoffensive ?

16voto

paradigmatic Points 20871

J'ai essayé de mettre en place un microbenchmark en utilisant l'excellente Scala-Benchmark-Template .

Il est très difficile d'écrire un benchmark significatif (non optimisé par le JIT) qui teste seulement les conversions implicites, donc j'ai dû ajouter un peu de surcharge.

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.

EDIT : J'ai utilisé scala 2.9.0 et java 1.6.0_24 (en mode serveur).

9voto

Daniel C. Sobral Points 159554

JVM peut optimiser les allocations d'objets supplémentaires, s'il détecte que cela serait utile.

C'est important, car si vous vous contentez d'inliner les choses, vous vous retrouvez avec des méthodes plus grandes, ce qui peut causer des problèmes de performance avec le cache ou même diminuer les chances que la JVM applique d'autres optimisations.

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