11

Estoy considerando usar Scala en un programa computacionalmente intensivo. El perfil de la versión C++ de nuestro código revela que podríamos beneficiarnos significativamente de la evaluación Lazy. Lo probé en Scala 2.9.1 y me gustó mucho. Sin embargo, cuando ejecuté la clase a través de un descompilador, la implementación no se veía del todo bien. Estoy asumiendo que es un artefacto de la decompilador, pero quería obtener una respuesta más concluyente ...¿Es esto un error en la implementación diferida de Scala 2.9.1 o simplemente un artefacto de descompilación?

considerar el siguiente ejemplo trivial:

class TrivialAngle(radians : Double) 
{ 
    lazy val sin = math.sin(radians) 
} 

cuando descompilarlo, me sale esto:

import scala.ScalaObject; 
import scala.math.package.; 
import scala.reflect.ScalaSignature; 

@ScalaSignature(bytes="omitted") 
public class TrivialAngle 
    implements ScalaObject 
{ 
    private final double radians; 
    private double sin; 
    public volatile int bitmap$0; 

    public double sin() 
    { 
    if ((this.bitmap$0 & 0x1) == 0); 
    synchronized (this) 
    { 
     if (
     (this.bitmap$0 & 0x1) == 0) 
     { 
     this.sin = package..MODULE$.sin(this.radians); 
     this.bitmap$0 |= 1; 
     } 
     return this.sin; 
    } 
    } 

    public TrivialAngle(double radians) 
    { 
    } 
} 

Para mí, el bloque de retorno está en el lugar equivocado, y siempre obtendrá el bloqueo. Esto no puede ser lo que está haciendo el código real, pero no puedo confirmarlo. ¿Alguien puede confirmar o negar que tengo una descompilación falsa, y que la implementación diferida es algo razonable (es decir, solo se bloquea cuando está calculando el valor y no adquiere el bloqueo para llamadas posteriores?)

¡Gracias!

Como referencia, este es el decompilador utilicé: http://java.decompiler.free.fr/?q=jdgui

+0

Computacionalmente intensivo y quieres estar haciendo bloqueos? –

+0

no, tengo muchos elementos que solo quiero calcular si los necesito, y me gustaría que esos resultados se almacenaran en caché después del cálculo. Dependiendo de la implementación, perezoso hace exactamente lo que me gustaría. Si pudiera especificar ningún bloqueo, sería incluso mejor, pero ese no es el punto de esta pregunta. – fbl

+1

Bueno, he hecho un montón de ajuste del código C/C++/Fortran computacionalmente intensivo (simulación de farma). El método que uso [es esto] (http://stackoverflow.com/questions/375913/what-can-i-use-to-profile-c-code-in-linux/378024#378024). (No siempre se puede creer en los perfiles, incluso cuando hablan con claridad.) –

Respuesta

9

scala -Xprint:jvm revela la verdadera historia:

[[syntax trees at end of jvm]]// Scala source: lazy.scala 
package <empty> { 
    class TrivialAngle extends java.lang.Object with ScalaObject { 
    @volatile protected var bitmap$0: Int = 0; 
    <paramaccessor> private[this] val radians: Double = _; 
    lazy private[this] var sin: Double = _; 
    <stable> <accessor> lazy def sin(): Double = { 
     if (TrivialAngle.this.bitmap$0.&(1).==(0)) 
     { 
      TrivialAngle.this.synchronized({ 
      if (TrivialAngle.this.bitmap$0.&(1).==(0)) 
       { 
       TrivialAngle.this.sin = scala.math.`package`.sin(TrivialAngle.this.radians); 
       TrivialAngle.this.bitmap$0 = TrivialAngle.this.bitmap$0.|(1); 
       () 
       }; 
      scala.runtime.BoxedUnit.UNIT 
      }); 
     () 
     }; 
     TrivialAngle.this.sin 
    }; 
    def this(radians: Double): TrivialAngle = { 
     TrivialAngle.this.radians = radians; 
     TrivialAngle.super.this(); 
    () 
    } 
    } 
} 

Es una (ya JVM 1.5) seguro y muy rápido, bloqueo doble comprobación.

Más detalles:

What's the (hidden) cost of Scala's lazy val?

tener en cuenta que si tiene varios miembros val perezosos en una clase, sólo uno de ellos puede ser inicializado a la vez, ya que están protegidos por synchronized(this) { ... }.

+0

¡Gracias! Si pudiera marcar 2 respuestas como "correctas", lo haría. Elegí esta porque es un poco más legible;) Soy plenamente consciente del hecho de que los campos vagos solo se pueden inicializar de a uno por vez. Esto puede, o no, influir en la forma en que diseñamos nuestra solución, pero ciertamente informa la decisión. – fbl

9

Lo que obtengo con javap -c no se corresponde con su descompilación. En particular, no hay un monitor ingrese cuando el campo se encuentre inicializado. Versión 2.9.1 también. Todavía existe la barrera de la memoria implícita por el acceso volátil del curso, por lo que no es completamente gratis. Comentarios comenzando con /// son míos

public double sin(); 
    Code: 
    0: aload_0 
    1: getfield  #14; //Field bitmap$0:I 
    4: iconst_1 
    5: iand 
    6: iconst_0 
    7: if_icmpne  54 /// if getField & 1 == O goto 54, skip lock 
    10: aload_0 
    11: dup 
    12: astore_1 
    13: monitorenter 
      /// 14 to 52 reasonably equivalent to synchronized block 
      /// in your decompiled code, without the return 
    53: monitorexit 
    54: aload_0 
    55: getfield  #27; //Field sin:D 
    58: dreturn  /// return outside lock 
    59: aload_1  /// (this would be the finally implied by the lock) 
    60: monitorexit 
    61: athrow 
    Exception table: 
    from to target type 
    14 54 59 any 
+0

Gracias! Si pudiera marcar 2 respuestas como "correctas", lo haría. Tanto esta respuesta como el retrónimo revelan la verdadera naturaleza de Lazy. – fbl

+0

No hay problema, yo prefiero la respuesta de retronym también, más que eso no es nuevo de esta opción -Xprint: jvm. javap aún podría ser el juez final si realmente no confías en scala, pero mejor si no llega a eso. –

Cuestiones relacionadas