简体   繁体   English

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

[英]Is this a bug in Scala 2.9.1 lazy implementation or just an artifact of decompilation

I am considering using Scala on a pretty computationally intensive program. 我正在考虑在一个计算密集的程序上使用Scala。 Profiling the C++ version of our code reveals that we could benefit significantly from Lazy evaluation. 对我们代码的C ++版本进行概要分析表明,我们可以从Lazy评估中获益。 I have tried it out in Scala 2.9.1 and really like it. 我已经在Scala 2.9.1中尝试过并且非常喜欢它。 However, when I ran the class through a decompiler the implemenation didn't look quite right. 但是,当我通过反编译器运行该类时,实现看起来并不正确。 I'm assuming that it's an artifact of the decompiler, but I wanted to get a more conclusive answer... 我假设它是反编译器的神器,但我想得到一个更确定的答案......

consider the following trivial example: 考虑以下简单的例子:

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

when I decompile it, I get this: 当我反编译它,我得到这个:

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

To me, the return block is in the wrong spot, and you will always acquire the lock. 对我来说,返回区块位于错误的位置,您将始终获得锁定。 This can't be what the real code is doing, but I am unable to confirm this. 这不是真正的代码所做的,但我无法证实这一点。 Can anyone confirm or deny that I have a bogus decompilation, and that the lazy implementation is somewhat reasonable (ie, only locks when it is computing the value, and doesn't acquire the lock for subsequent calls?) 任何人都可以确认或否认我有一个虚假的反编译,并且懒惰的实现有点合理(即,只有在计算值时才锁定,并且不会为后续调用获取锁定?)

Thanks! 谢谢!

For reference, this is the decompiler I used: http://java.decompiler.free.fr/?q=jdgui 作为参考,这是我使用的反编译器: http//java.decompiler.free.fr/?q = jdgui

What I get with javap -c does not correspond to your decompile. 我用javap -c得到的东西与你的反编译不符。 In particular, there is no monitor enter when the field is found to be initialized. 特别是,当发现字段被初始化时,没有监视器输入。 Version 2.9.1 too. 版本2.9.1也是。 There is still the memory barrier implied by the volatile access of course, so it does not come completely free. 当然,挥发性访问仍然存在内存障碍,因此它并非完全免费。 Comments starting with /// are mine ///开头的评论是我的

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

scala -Xprint:jvm reveals the true story: 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();
      ()
    }
  }
}

It's a (since JVM 1.5) safe, and very fast, double checked lock. 这是一个(自JVM 1.5以来)安全且非常快速的双重检查锁。

More details: 更多细节:

What's the (hidden) cost of Scala's lazy val? Scala慵懒的val的(隐藏)成本是多少?

Be aware that if you have multiple lazy val members in a class, only one of them can be initialized at once, as they are guarded by synchronized(this) { ... } . 请注意,如果一个类中有多个lazy val成员,则只能同时初始化其中一个成员,因为它们由synchronized(this) { ... }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM