这是Scala 2.9.1延迟实现中的错误还是反编译的工件

时间:2011-10-22 20:28:24

标签: performance scala synchronization lazy-evaluation decompiling

我正在考虑在一个计算密集的程序上使用Scala。对我们代码的C ++版本进行概要分析表明,我们可以从Lazy评估中获益。我已经在Scala 2.9.1中尝试过并且非常喜欢它。但是,当我通过反编译器运行该类时,实现看起来并不正确。我假设它是反编译器的神器,但我想得到一个更确定的答案......

考虑以下简单的例子:

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

当我反编译它时,我明白了:

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

对我来说,返回区块位于错误的位置,您将始终获得锁定。这不是真正的代码所做的,但我无法证实这一点。任何人都可以确认或否认我有一个伪造的反编译,并且懒惰的实现有点合理(即,只有在计算值时锁定,并且没有为后续调用获取锁定?)

谢谢!

作为参考,这是我使用的反编译器: http://java.decompiler.free.fr/?q=jdgui

2 个答案:

答案 0 :(得分:9)

我使用javap -c获得的内容与您的反编译不符。特别是,当发现字段被初始化时,没有监视器输入。版本2.9.1也是。当然,挥发性访问仍然存在内存障碍,因此它并非完全免费。以///开头的评论是我的

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

答案 1 :(得分:9)

scala -Xprint:jvm揭示了真实的故事:

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

这是一个(自JVM 1.5起)安全且非常快速的双重检查锁。

更多详情:

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

请注意,如果一个类中有多个lazy val成员,则只能同时初始化其中一个成员,因为它们由synchronized(this) { ... }保护。