web-dev-qa-db-fra.com

Comment mesurer le temps d'une déclaration dans la console scala?

J'utilise Scala pour mesurer les performances du moteur regex de Java. L'expression rationnelle ci-dessous s'exécute en environ 3 secondes, mais je ne suis pas encore capable de la mesurer à l'aide de System.currentTimeMillis. (la dernière expression renvoie 0)

scala> val b = System.currentTimeMillis; val v = new Regex("(x+)+y").findAllIn("x"*25); b-System.currentTimeMillis
b: Long = 1330787275629
v: scala.util.matching.Regex.MatchIterator = empty iterator
res18: Long = 0

Pourquoi est-ce que la dernière valeur renvoyée est 0 et non pas la quantité de ms que scala dépense pour exécuter l’expression rationnelle?

30
Piotr Czapla

La durée inexpliquée provient de l'appel de REPL toString sur l'itérateur renvoyé de findAllIn. Ceci appelle à son tour Regex.MatchIterator#hasNext , ce qui déclenche la recherche.

scala> def time[A](a: => A) = {
     |   val now = System.nanoTime
     |   val result = a
     |   val micros = (System.nanoTime - now) / 1000
     |   println("%d microseconds".format(micros))
     |   result
     | }
time: [A](a: => A)A

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._, definitions._ also imported    **
** Try  :help, :vals, power.<tab>           **

scala> :wrap time
Set wrapper to 'time'

scala> new Regex("(x+)+y").findAllIn("x"*25).toString
3000737 microseconds
res19: String = empty iterator

scala> {new Regex("(x+)+y").findAllIn("x"*25); 0}
582 microseconds
res20: Int = 0
81
retronym
def time[A](f: => A) = {
  val s = System.nanoTime
  val ret = f
  println("time: "+(System.nanoTime-s)/1e6+"ms")
  ret
}

Utilisez-le avec:

scala> time { 10*2 }
time: 0.054212ms
res1: Int = 20
28
kiritsuku

C'est assez intéressant! J'ai ajouté un println("start") et un "end" autour de la ligne qui crée le regex et ai exécuté le code - ceci affiche 

start 
end

puis fait une pause d'environ trois secondes avant d'imprimer le reste de la sortie. 

Il semble donc que ce qui se passe si la regex est en train d'être créée mais ne sera pas exécutée avant que toString ne soit appelé afin de le générer sur la console. Pour que le test fonctionne, ajoutez un appel manuel toString avant de calculer le temps passé. 

scala> val b = System.currentTimeMillis; val v = new scala.util.matching.Regex("(x+)+y").findAllIn("x"*25); v.toString; System.currentTimeMillis-b
b: Long = 1330789547209
v: scala.util.matching.Regex.MatchIterator = empty iterator
res14: Long = 4881

En outre, il devrait être System.currentTimeMillis-b plutôt que l'inverse ...

5
Russell

Ce n'est pas une réponse directe à votre question, mais vous voudrez peut-être envisager d'utiliser une bibliothèque de benchmarking mature comme Criterium .

De nombreux écueils sont associés à l’analyse comparative en général et à la JVM en particulier ( ceci est une bonne discussion). Ce n'est pas trivial de les éviter si vous lancez votre propre solution.

2
Paul Butcher

Une légère amélioration pourrait inclure plusieurs exécutions. Une bibliothèque d'analyse comparative appropriée est vraiment importante si vous vous souciez de plus que des différences de vitesse relatives.

def time[A](a: => A, n:Int) = {
    var times = List[Long]()
        for (_ <- 1 to n) {
        val now = System.nanoTime
        val res = a
        times :::= List(System.nanoTime - now)
    }
    val result = times.sum / n
    println("%d microseconds".format(result / 1000))
    result
}
1
ramatevish

Considérez aussi cette approche pour renvoyer le résultat réel de la méthode appliquée ainsi que le temps écoulé, à la fois dans un tuple (Scala 2.10+), 

implicit class RichElapsed[A](f: => A) {

  def elapsed(): (A, Double) = {
    val start = System.nanoTime()
    val res = f
    val end = System.nanoTime()

    (res, (end-start)/1e3)
  }

}

Pour toute fonction donnée def f(n: Int) = (1 to n) product,

scala> val (res, time) = f(3).elapsed
res: Int = 6
time: Double = 46.4378
0
elm