Scala的延迟val有哪些“隐藏”的成本?

48 浏览
0 Comments

Scala的延迟val有哪些“隐藏”的成本?

Scala的一个方便的特性是lazy val,其中val的评估被延迟到它必须(在第一次访问时)才执行。

当然,一个lazy val必须有一些开销-Scala必须在某处跟踪值是否已经被评估,并且评估必须同步,因为多个线程可能同时尝试访问值进行第一次访问。

那么lazy val的成本到底是多少-是否有一个隐藏的布尔标志与lazy val相关,以跟踪它是否已经被评估,具体的同步是什么,还有其他成本吗?

另外,假设我这样做:

class Something {
    lazy val (x, y) = { ... }
}

这是否与拥有两个单独的lazy val xy相同,或者我只获得一次开销,对于对(x,y)成对的延迟值?

admin 更改状态以发布 2023年5月24日
0
0 Comments

看起来编译器安排了一个类级位图int字段来标记多个延迟字段是否已初始化,并在相关的异或位图指示需要时在同步块中初始化目标字段。

使用:

class Something {
  lazy val foo = getFoo
  def getFoo = "foo!"
}

会生成示例字节码:

 0  aload_0 [this]
 1  getfield blevins.example.Something.bitmap$0 : int [15]
 4  iconst_1
 5  iand
 6  iconst_0
 7  if_icmpne 48
10  aload_0 [this]
11  dup
12  astore_1
13  monitorenter
14  aload_0 [this]
15  getfield blevins.example.Something.bitmap$0 : int [15]
18  iconst_1
19  iand
20  iconst_0
21  if_icmpne 42
24  aload_0 [this]
25  aload_0 [this]
26  invokevirtual blevins.example.Something.getFoo() : java.lang.String [18]
29  putfield blevins.example.Something.foo : java.lang.String [20]
32  aload_0 [this]
33  aload_0 [this]
34  getfield blevins.example.Something.bitmap$0 : int [15]
37  iconst_1
38  ior
39  putfield blevins.example.Something.bitmap$0 : int [15]
42  getstatic scala.runtime.BoxedUnit.UNIT : scala.runtime.BoxedUnit [26]
45  pop
46  aload_1
47  monitorexit
48  aload_0 [this]
49  getfield blevins.example.Something.foo : java.lang.String [20]
52  areturn
53  aload_1
54  monitorexit
55  athrow

值以lazy val (x, y) = { ... }的形式初始化的元组通过相同的机制进行嵌套缓存。元组结果是懒惰地评估和缓存的,而访问x或y将触发元组的评估。从元组中提取各个值是独立地和懒惰地完成的(并缓存)。因此上面的双重实例化代码会生成一个类型为Tuple2xyx$1字段。

0
0 Comments

这是从Scala邮件列表中提取出来的内容,它描述了lazy的实现细节,使用Java代码(而不是字节码):

class LazyTest {
  lazy val msg = "Lazy"
}

会被编译成与以下Java代码类似的内容:

class LazyTest {
  public int bitmap$0;
  private String msg;
  public String msg() {
    if ((bitmap$0 & 1) == 0) {
        synchronized (this) {
            if ((bitmap$0 & 1) == 0) {
                synchronized (this) {
                    msg = "Lazy";
                }
            }
            bitmap$0 = bitmap$0 | 1;
        }
    }
    return msg;
  }
}

0