11

J'envisage d'utiliser Scala sur un joli programme de calcul intensif. Le profilage de la version C++ de notre code révèle que nous pourrions bénéficier de manière significative de l'évaluation paresseuse. Je l'ai essayé dans Scala 2.9.1 et l'aime vraiment. Cependant, quand j'ai couru la classe à travers un décompilateur, la mise en œuvre ne semblait pas très bien. Je suppose que c'est un artefact de la décompilateur, mais je voulais obtenir une réponse plus concluante ...Est-ce un bug dans Scala 2.9.1 mise en œuvre paresseuse ou juste un artefact de décompilation

considérer l'exemple trivial suivant:

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

quand je décompiler, je reçois ceci:

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

Pour moi, le bloc de retour est au mauvais endroit, et vous obtiendrez toujours le verrou. Cela ne peut pas être ce que fait le vrai code, mais je suis incapable de le confirmer. Est-ce que quelqu'un peut confirmer ou nier que j'ai une fausse décompilation, et que l'implémentation paresseuse est quelque peu raisonnable (ie, verrouille uniquement quand elle calcule la valeur, et n'acquiert pas le verrou pour les appels suivants?)

Merci!

Pour référence, c'est le décompilateur je: http://java.decompiler.free.fr/?q=jdgui

+0

Vous travaillez intensivement et vous voulez faire des verrous? –

+0

non, j'ai beaucoup d'éléments que je veux seulement calculer si/quand j'ai besoin d'eux, et je voudrais que ces résultats mis en cache après le calcul. Selon la mise en œuvre, paresseux fait exactement ce que je voudrais. Si je ne pouvais pas spécifier de verrouillage, ce serait encore mieux, mais ce n'est pas le but de cette question. – fbl

+1

Eh bien, j'ai fait beaucoup de réglage de code C/C++/Fortran intensif en calcul (simulation pharma). La méthode que j'utilise [est ceci] (http://stackoverflow.com/questions/375913/what-can-i-use-to-profile-c-code-in-linux/378024#378024). (Vous ne pouvez pas toujours croire les profileurs, même s'ils parlent clairement.) –

Répondre

9

scala -Xprint:jvm révèle l'histoire vraie:

[[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(); 
    () 
    } 
    } 
} 

Il est un (depuis JVM 1.5) en toute sécurité, et très rapide, serrure revérifié.

Plus de détails:

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

Sachez que si vous avez plusieurs membres de val paresseux dans une classe, une seule d'entre elles peut être initialisé à la fois, car ils sont gardés par synchronized(this) { ... }.

+0

Merci! Si je pouvais marquer 2 réponses comme «correctes», je le ferais. J'ai choisi celui-ci parce qu'il est légèrement plus lisible;) Je suis pleinement conscient du fait que les champs paresseux ne peuvent être initialisés qu'un à la fois. Cela peut ou non influencer la façon dont nous concevons notre solution, mais cela influence certainement la décision. – fbl

9

Ce que je reçois avec javap -c ne correspond pas à votre décompiler. En particulier, il n'y a pas d'entrée de moniteur lorsque le champ est initialisé. Version 2.9.1 aussi. Il y a toujours la barrière de la mémoire impliquée par l'accès volatile bien sûr, de sorte qu'il ne soit pas complètement libre. Commentaires commençant par /// sont les miens

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

Merci! Si je pouvais marquer 2 réponses comme «correctes», je le ferais. Tant cette réponse que les retronym révèlent la vraie nature de Lazy. – fbl

+0

Pas de problème, je préfère aussi la réponse de Retronym, d'autant plus que je n'ai pas de nouveauté de cette option -Xprint: jvm. javap pourrait encore être le juge final si vous ne faites vraiment pas confiance à scala, mais mieux si cela ne vient pas à ça. –

Questions connexes