wp_get_post_tags the_tags tag name get_the_tags first codex all function scala profiling aspect

function - the_tags - Cómo perfil de métodos en Scala?



wordpress get first tag (11)

¿Cuál es una forma estándar de crear perfiles de llamadas al método Scala?

Lo que necesito son ganchos alrededor de un método, con el cual puedo usar para iniciar y detener los temporizadores.

En Java utilizo la programación de aspecto, aspectJ, para definir los métodos que se van a perfilar e inyecto bytecode para lograr lo mismo.

¿Existe una forma más natural en Scala, donde puedo definir un conjunto de funciones para llamar antes y después de una función sin perder ningún tipo de tipado estático en el proceso?


¿Desea hacer esto sin cambiar el código para el que desea medir los tiempos? Si no te importa cambiar el código, entonces podrías hacer algo como esto:

def time[R](block: => R): R = { val t0 = System.nanoTime() val result = block // call-by-name val t1 = System.nanoTime() println("Elapsed time: " + (t1 - t0) + "ns") result } // Now wrap your method calls, for example change this... val result = 1 to 1000 sum // ... into this val result = time { 1 to 1000 sum }


Además de la respuesta de Jesper, puede ajustar automáticamente las invocaciones de métodos en REPL:

scala> def time[R](block: => R): R = { | val t0 = System.nanoTime() | val result = block | println("Elapsed time: " + (System.nanoTime - t0) + "ns") | result | } time: [R](block: => R)R

Ahora - envolvemos cualquier cosa en esto

scala> :wrap time wrap: no such command. Type :help for help.

OK - tenemos que estar en el modo de potencia

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

Envolver

scala> :wrap time Set wrapper to ''time'' scala> BigDecimal("1.456") Elapsed time: 950874ns Elapsed time: 870589ns Elapsed time: 902654ns Elapsed time: 898372ns Elapsed time: 1690250ns res0: scala.math.BigDecimal = 1.456

No tengo idea de por qué ese material impreso salió 5 veces

Actualización a partir de 2.12.2:

scala> :pa // Entering paste mode (ctrl-D to finish) package wrappers { object wrap { def apply[A](a: => A): A = { println("running...") ; a } }} // Exiting paste mode, now interpreting. scala> $intp.setExecutionWrapper("wrappers.wrap") scala> 42 running... res2: Int = 42


Esto es lo que uso:

import System.nanoTime def profile[R](code: => R, t: Long = nanoTime) = (code, nanoTime - t) // usage: val (result, time) = profile { /* block of code to be profiled*/ } val (result2, time2) = profile methodToBeProfiled(foo)


Hay tres bibliotecas de benchmarking para Scala de las que puede hacer uso.

Dado que es probable que cambien las URL en el sitio vinculado, pegaré el contenido relevante a continuación.

  1. SPerformance : marco de pruebas de rendimiento para comparar SPerformance pruebas de rendimiento y trabajar dentro de Simple Build Tool.

  2. scala-benchmarking-template - Proyecto de plantilla SBT para crear puntos de referencia Scala (micro-) basados ​​en Caliper.

  3. Metrics : captura de JVM y métricas a nivel de aplicación. Entonces sabes lo que está pasando


Me gusta la simplicidad de la respuesta de @ wrick, pero también quería:

  • el generador de perfiles maneja el bucle (por consistencia y conveniencia)

  • tiempo más preciso (usando nanoTime)

  • tiempo por iteración (no tiempo total de todas las iteraciones)

  • simplemente devuelve ns / iteración - no una tupla

Esto se logra aquí:

def profile[R] (repeat :Int)(code: => R, t: Long = System.nanoTime) = { (1 to repeat).foreach(i => code) (System.nanoTime - t)/repeat }

Para una mayor precisión, una simple modificación permite un ciclo de calentamiento de punto de acceso JVM (no temporizado) para temporizar pequeños fragmentos:

def profile[R] (repeat :Int)(code: => R) = { (1 to 10000).foreach(i => code) // warmup val start = System.nanoTime (1 to repeat).foreach(i => code) (System.nanoTime - start)/repeat }


Mientras está de pie sobre los hombros de gigantes ...

Una biblioteca sólida de terceros sería más ideal, pero si necesita algo rápido y basado en la biblioteca estándar, la siguiente variante ofrece:

  • Repeticiones
  • El último resultado gana por repeticiones múltiples
  • Tiempo total y tiempo promedio para repeticiones múltiples
  • Elimina la necesidad de tiempo / proveedor instantáneo como un param

.

import scala.concurrent.duration._ import scala.language.{postfixOps, implicitConversions} package object profile { def profile[R](code: => R): R = profileR(1)(code) def profileR[R](repeat: Int)(code: => R): R = { require(repeat > 0, "Profile: at least 1 repetition required") val start = Deadline.now val result = (1 until repeat).foldLeft(code) { (_: R, _: Int) => code } val end = Deadline.now val elapsed = ((end - start) / repeat) if (repeat > 1) { println(s"Elapsed time: $elapsed averaged over $repeat repetitions; Total elapsed time") val totalElapsed = (end - start) println(s"Total elapsed time: $totalElapsed") } else println(s"Elapsed time: $elapsed") result } }

También vale la pena señalar que puede utilizar el método Duration.toCoarsest para convertir a la mayor unidad de tiempo posible, aunque no estoy seguro de lo fácil que es esto con una pequeña diferencia de tiempo entre ejecuciones, por ejemplo

Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60). Type in expressions to have them evaluated. Type :help for more information. scala> import scala.concurrent.duration._ import scala.concurrent.duration._ scala> import scala.language.{postfixOps, implicitConversions} import scala.language.{postfixOps, implicitConversions} scala> 1000.millis res0: scala.concurrent.duration.FiniteDuration = 1000 milliseconds scala> 1000.millis.toCoarsest res1: scala.concurrent.duration.Duration = 1 second scala> 1001.millis.toCoarsest res2: scala.concurrent.duration.Duration = 1001 milliseconds scala>


Puede usar System.currentTimeMillis :

def time[R](block: => R): R = { val t0 = System.currentTimeMillis() val result = block // call-by-name val t1 = System.currentTimeMillis() println("Elapsed time: " + (t1 - t0) + "ms") result }

Uso:

time{ //execute somethings here, like methods, or some codes. }

nanoTime te mostrará ns , por lo que será difícil de ver. Por lo tanto, le sugiero que puede usar currentTimeMillis en lugar de hacerlo.


Tomé la solución de Jesper y agregué una agregación a ella en varias ejecuciones del mismo código

def time[R](block: => R) = { def print_result(s: String, ns: Long) = { val formatter = java.text.NumberFormat.getIntegerInstance println("%-16s".format(s) + formatter.format(ns) + " ns") } var t0 = System.nanoTime() var result = block // call-by-name var t1 = System.nanoTime() print_result("First Run", (t1 - t0)) var lst = for (i <- 1 to 10) yield { t0 = System.nanoTime() result = block // call-by-name t1 = System.nanoTime() print_result("Run #" + i, (t1 - t0)) (t1 - t0).toLong } print_result("Max", lst.max) print_result("Min", lst.min) print_result("Avg", (lst.sum / lst.length)) }

Supongamos que desea counter_new dos funciones counter_new y counter_old , el siguiente es el uso:

scala> time {counter_new(lst)} First Run 2,963,261,456 ns Run #1 1,486,928,576 ns Run #2 1,321,499,030 ns Run #3 1,461,277,950 ns Run #4 1,299,298,316 ns Run #5 1,459,163,587 ns Run #6 1,318,305,378 ns Run #7 1,473,063,405 ns Run #8 1,482,330,042 ns Run #9 1,318,320,459 ns Run #10 1,453,722,468 ns Max 1,486,928,576 ns Min 1,299,298,316 ns Avg 1,407,390,921 ns scala> time {counter_old(lst)} First Run 444,795,051 ns Run #1 1,455,528,106 ns Run #2 586,305,699 ns Run #3 2,085,802,554 ns Run #4 579,028,408 ns Run #5 582,701,806 ns Run #6 403,933,518 ns Run #7 562,429,973 ns Run #8 572,927,876 ns Run #9 570,280,691 ns Run #10 580,869,246 ns Max 2,085,802,554 ns Min 403,933,518 ns Avg 797,980,787 ns

Espero que esto sea útil


Utilizo una técnica que es fácil de mover en bloques de código. El quid de la cuestión es que comienza la misma línea exacta y finaliza el temporizador, por lo que en realidad es una simple copia y pegado. La otra cosa agradable es que puedes definir lo que el tiempo significa para ti como una cadena, todo en la misma línea.

Ejemplo de uso:

Timelog("timer name/description") //code to time Timelog("timer name/description")

El código:

object Timelog { val timers = scala.collection.mutable.Map.empty[String, Long] // // Usage: call once to start the timer, and once to stop it, using the same timer name parameter // def timer(timerName:String) = { if (timers contains timerName) { val output = s"$timerName took ${(System.nanoTime() - timers(timerName)) / 1000 / 1000} milliseconds" println(output) // or log, or send off to some performance db for analytics } else timers(timerName) = System.nanoTime() }

Pros:

  • no es necesario ajustar el código como un bloque o manipular dentro de las líneas
  • puede mover fácilmente el inicio y el final del temporizador entre las líneas de código al ser exploratorio

Contras:

  • menos brillante para el código completamente funcional
  • Obviamente, este objeto filtra las entradas del mapa si no "cierra" los temporizadores, por ejemplo, si su código no llega a la segunda invocación para un inicio de temporizador determinado.

ScalaMeter es una buena biblioteca para realizar benchmarking en Scala

A continuación se muestra un ejemplo simple

import org.scalameter._ def sumSegment(i: Long, j: Long): Long = (i to j) sum val (a, b) = (1, 1000000000) val execution_time = measure { sumSegment(a, b) }

Si ejecuta el fragmento de código anterior en la hoja de trabajo de Scala obtendrá el tiempo de ejecución en milisegundos

execution_time: org.scalameter.Quantity[Double] = 0.260325 ms


testing.Benchmark podría ser útil.

scala> def testMethod {Thread.sleep(100)} testMethod: Unit scala> object Test extends testing.Benchmark { | def run = testMethod | } defined module Test scala> Test.main(Array("5")) $line16.$read$$iw$$iw$Test$ 100 100 100 100 100