performance optimization scala

performance - ¿Por qué un pequeño cambio en este código de Scala hace una gran diferencia en el rendimiento?



optimization (2)

Estoy ejecutando un sistema Debian 6.0 (Squeeze) de 32 bits (una CPU Core 2 de 2.5 GHz), Sun-java6 6.24-1 pero con los paquetes Scala 2.8.1 de Wheezy.

Este código, compilado con scalac -optimise , toma más de 30 segundos para ejecutarse:

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) } }

Pero si hago el cambio trivial de mover los val es= una línea hacia abajo y dentro del alcance de main , entonces se ejecuta en solo 1 segundo, que es mucho más de lo que esperaba ver y comparable con el rendimiento de C ++ equivalente. Curiosamente, dejar los val es= donde está pero calificarlo con lazy también tiene el mismo efecto de aceleración.

¿Que está pasando aqui? ¿Por qué el cálculo fuera del alcance de la función es mucho más lento?


El código dentro de un bloque de objetos de nivel superior se traduce a un inicializador estático en la clase del objeto. El equivalente en Java sería

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

HotSpot JVM no realiza ninguna optimización en el código encontrado durante los inicializadores estáticos, bajo la heurística razonable de que dicho código solo se ejecutará una vez.


La JVM no optimiza los inicializadores estáticos (que es lo que es) al mismo nivel que optimiza las llamadas a métodos. Lamentablemente, cuando trabajas mucho allí, eso perjudica el rendimiento; este es un ejemplo perfecto de eso. Esta es también una de las razones por las que el antiguo rasgo de Application se consideró problemático, y por qué hay en Scala 2.9 un rasgo de DelayedInit que ayuda un poco al compilador para mover cosas desde el inicializador a un método que se llama más adelante.

(Editar: se corrigió "constructor" a "inicializador". ¡Error bastante largo!)