57 votes

Pourquoi une petite modification de ce code Scala fait-elle une si grande différence en termes de performances ?

Je fonctionne sur un ordinateur 32 bits Debian 6.0 (Squeeze) (un système à 2,5 GHz). Core 2 CPU), sun-java6 6.24-1 mais avec les paquets Scala 2.8.1 de Wheezy.

Ce code, compilé avec scalac -optimise prend plus de 30 secondes pour fonctionner :

object Performance {

  import scala.annotation.tailrec

  @tailrec def gcd(x:Int,y:Int):Int = {
    if (x == 0)
      y 
    else 
      gcd(y%x,x)
  }

  val p = 1009
  val q = 3643
  val t = (p-1)*(q-1)

  val es = (2 until t).filter(gcd(_,t) == 1)
  def main(args:Array[String]) {
    println(es.length)
  }
}

Mais si je fais le changement trivial de déplacer le val es= une ligne plus bas et à l'intérieur de la portée de main Il s'exécute alors en une seconde seulement, ce qui est beaucoup plus conforme à ce que j'attendais et comparable aux performances d'un C++ équivalent. Il est intéressant de noter qu'en laissant le val es= où il est, mais en le qualifiant de lazy a également le même effet accélérateur.

Qu'est-ce qui se passe ici ? Pourquoi l'exécution du calcul en dehors de la portée de la fonction est-elle tellement plus lente ?

51voto

Rex Kerr Points 94401

La JVM n'optimise pas les initialisateurs statiques (ce dont il s'agit) au même niveau que les appels de méthode. Malheureusement, lorsque vous y effectuez beaucoup de travail, les performances s'en ressentent - ceci en est un exemple parfait. C'est également l'une des raisons pour lesquelles l'ancien Application a été considéré comme problématique, et c'est la raison pour laquelle il existe dans Scala 2.9 un caractère DelayedInit qui bénéficie d'un peu d'aide du compilateur pour déplacer des éléments de l'initialisateur vers une méthode qui sera appelée plus tard.


(Edit : correction de "constructor" en "initializer". typo plutôt longue !)

40voto

Dave Griffith Points 12923

Le code à l'intérieur d'un bloc objet de haut niveau est traduit en un initialisateur statique sur la classe de l'objet. L'équivalent en Java serait

class Performance{
    static{
      //expensive calculation
    }
    public static void main(String[] args){
      //use result of expensive calculation
    }
}

La JVM HotSpot n'effectue aucune optimisation sur le code rencontré lors des initialisations statiques, sous l'heuristique raisonnable que ce code ne sera exécuté qu'une seule fois.

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