[英]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.