Какова (скрытая) стоимость ленивого val Scala?

Одна удобная функция Scala lazy val, где оценка a val отложен, пока это не необходимо (в первом доступе).

Конечно, a lazy val должен иметь немного служебные - где-нибудь, Scala должен отслеживать то, было ли значение уже оценено, и оценка должна синхронизироваться, потому что несколько потоков могли бы попытаться получить доступ к значению впервые одновременно.

Что точно является стоимостью a lazy val - есть ли скрытый булев флаг, связанный с a lazy val отслеживать, если это было оценено или нет, что точно синхронизируется и там еще затраты?

Кроме того, предположите, что я делаю это:

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

Это то же как наличие двух отдельных lazy vals x и y или сделайте я получаю издержки только однажды для пары (x, y)?

160
задан Peter Mortensen 24 April 2013 в 18:13
поделиться

2 ответа

Это взято из списка рассылки 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;
  }

}
81
ответ дан 23 November 2019 в 21:33
поделиться

Похоже, что компилятор организует для поля int растрового изображения уровня класса пометку нескольких ленивых полей как инициализированных (или нет) и инициализирует целевое поле в синхронизированном блоке, если соответствующий xor растровое изображение указывает, что это необходимо.

Использование:

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 запускает оценку кортежа. Извлечение отдельного значения из кортежа выполняется независимо и лениво (и кэшируется). Таким образом, приведенный выше код с двойным экземпляром генерирует поле x , y и поле x $ 1 типа Tuple2 .

38
ответ дан 23 November 2019 в 21:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: