Performance 这是Scala2.9.1延迟实现中的一个bug,还是反编译的产物
我正在考虑在一个计算量很大的程序上使用Scala。剖析代码的C++版本表明,我们可以从懒惰的评估中获益匪浅。我已经在Scala 2.9.1中试用过,非常喜欢它。然而,当我通过反编译器运行该类时,实现看起来并不完全正确。我假设这是反编译器的产物,但我想得到一个更确切的答案 考虑以下简单的例子:Performance 这是Scala2.9.1延迟实现中的一个bug,还是反编译的产物,performance,scala,synchronization,lazy-evaluation,decompiling,Performance,Scala,Synchronization,Lazy Evaluation,Decompiling,我正在考虑在一个计算量很大的程序上使用Scala。剖析代码的C++版本表明,我们可以从懒惰的评估中获益匪浅。我已经在Scala 2.9.1中试用过,非常喜欢它。然而,当我通过反编译器运行该类时,实现看起来并不完全正确。我假设这是反编译器的产物,但我想得到一个更确切的答案 考虑以下简单的例子: class TrivialAngle(radians : Double) { lazy val sin = math.sin(radians) } 当我反编译它时,我得到以下结果: import
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)
{
}
}
对我来说,返回块位于错误的位置,您将始终获得锁。这不可能是真正的代码在做什么,但我无法确认这一点。有人能确认或否认我有一个虚假的反编译,并且延迟实现在某种程度上是合理的(即,只在计算值时锁定,而不为后续调用获取锁定?)
谢谢
作为参考,这是我使用的反编译器:
我用
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
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末尾的语法树]]//Scala源代码:lazy.Scala
包装{
类使用ScalaObject扩展java.lang.Object{
@易失性保护变量位图$0:Int=0;
private[this]val弧度:Double=\uux;
lazy-private[this]变量sin:Double=\u0;
请注意,如果一个类中有多个惰性val成员,那么一次只能初始化其中一个,因为它们由synchronized(this){…}保护
计算密集型,您想进行锁定吗?不,我有很多项,我只想在需要时进行计算,并且我希望在计算后缓存这些结果。根据实现情况,lazy会完全满足我的要求。如果我可以指定不锁定,那会更好,但这不是问题所在问题的关键。我已经对计算密集型C/C++/Fortran代码(制药模拟)做了大量调整。我使用的方法。(你不能总是相信分析器,即使它们说得很清楚。)Scala代码无法反编译为Java。它使用了有效字节码的功能,但没有Java等价物。我不是说这里发生的事情就是这样(尽管可能是这样)但是,如果你必须读字节码,java java C++分析器在“采样”模式中使用的非常有效。你知道一个好的分析器,它可以为Java堆栈采样吗?我过去使用的技术相当成功,但是我更喜欢配置文件。感谢!如果我能将2个响应标记为“正确”,我会的。这个答案和retronym的答案都揭示了懒惰的真正本质。没问题,我也更喜欢retronym的答案,更何况我对-Xprint:jvm选项并不陌生。如果你真的不信任scala,javap可能仍然是最终的判断,但如果它不信任,那就更好了说到这里。谢谢!如果我能将2个响应标记为“正确”,我会的。我选择这一个是因为它更可读;)我完全知道一个事实,即惰性字段一次只能初始化一个。这可能会,也可能不会影响我们构建解决方案的方式,但它肯定会通知决策。