Jaki jest (ukryty) koszt lazy val Scali?
Jedną z przydatnych funkcji Scali Jest lazy val
, Gdzie ocena {[2] } jest opóźniona, dopóki nie jest to konieczne (przy pierwszym dostępie).
Oczywiście, lazy val
musi mieć jakieś narzuty - gdzieś Scala musi śledzić, czy wartość została już oceniona i ocena musi być zsynchronizowana, ponieważ wiele wątków może próbować uzyskać dostęp do wartości po raz pierwszy w tym samym czasie.
Jaki jest dokładnie koszt lazy val
- Czy istnieje ukryta flaga logiczna powiązane z lazy val
, aby śledzić, czy został oceniony, czy nie, co dokładnie jest zsynchronizowane i czy są jakieś dodatkowe koszty?
Ponadto Załóżmy, że robię to:
class Something {
lazy val (x, y) = { ... }
}
Czy to to samo, co posiadanie dwóch oddzielnych lazy val
s x
i y
czy tylko raz, dla pary (x, y)
?
6 answers
Jest to zaczerpnięte z listy dyskusyjnej scala i podaje szczegóły implementacji lazy
w kategoriach kodu Javy (a nie kodu bajtowego):
class LazyTest {
lazy val msg = "Lazy"
}
Jest skompilowane do czegoś równoważnego Następującemu kodowi Javy:
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-20 20:33:23
Wygląda na to, że kompilator organizuje pole int mapy bitowej na poziomie klasy, aby oznaczyć wiele leniwych pól jako zainicjowane (lub nie) i inicjalizuje pole docelowe w zsynchronizowanym bloku, jeśli odpowiedni xor mapy bitowej wskazuje, że jest to konieczne.
Użycie:
class Something {
lazy val foo = getFoo
def getFoo = "foo!"
}
Wyświetla przykładowy kod bajtowy:
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
Wartości inicjowane krotkami takimi jak lazy val (x,y) = { ... }
mają zagnieżdżone buforowanie za pomocą tego samego mechanizmu. Wynik krotki jest leniwie oceniany i buforowany, a dostęp X lub y będzie Uruchom ocenę krotki. Ekstrakcja indywidualnej wartości z krotki jest wykonywana niezależnie i leniwie (i buforowana). Tak więc powyższy kod podwójnej instancji generuje x
, y
, oraz pole x$1
typu Tuple2
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-14 23:22:38
W Scali 2.10, wartość leniwa jak:
class Example {
lazy val x = "Value";
}
Jest skompilowany do kodu bajtowego, który przypomina następujący kod Java:
public class Example {
private String x;
private volatile boolean bitmap$0;
public String x() {
if(this.bitmap$0 == true) {
return this.x;
} else {
return x$lzycompute();
}
}
private String x$lzycompute() {
synchronized(this) {
if(this.bitmap$0 != true) {
this.x = "Value";
this.bitmap$0 = true;
}
return this.x;
}
}
}
Zwróć uwagę, że bitmapa jest reprezentowana przez boolean
. Jeśli dodasz kolejne pole, kompilator zwiększy rozmiar pola, aby móc reprezentować co najmniej 2 wartości, np. jako byte
. To się dzieje na wielkich zajęciach.
x
jest zapisywana w pamięci. Ten artykuł na blogu daje Wyjaśnienie.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-25 19:21:17
Scala SIP-20 proponuje nową implementację Lazy val, która jest bardziej poprawna, ale ~25% wolniejsza niż" aktualna " wersja.
Proponowane wdrożenie wygląda następująco:
class LazyCellBase { // in a Java file - we need a public bitmap_0
public static AtomicIntegerFieldUpdater<LazyCellBase> arfu_0 =
AtomicIntegerFieldUpdater.newUpdater(LazyCellBase.class, "bitmap_0");
public volatile int bitmap_0 = 0;
}
final class LazyCell extends LazyCellBase {
import LazyCellBase._
var value_0: Int = _
@tailrec final def value(): Int = (arfu_0.get(this): @switch) match {
case 0 =>
if (arfu_0.compareAndSet(this, 0, 1)) {
val result = 0
value_0 = result
@tailrec def complete(): Unit = (arfu_0.get(this): @switch) match {
case 1 =>
if (!arfu_0.compareAndSet(this, 1, 3)) complete()
case 2 =>
if (arfu_0.compareAndSet(this, 2, 3)) {
synchronized { notifyAll() }
} else complete()
}
complete()
result
} else value()
case 1 =>
arfu_0.compareAndSet(this, 1, 2)
synchronized {
while (arfu_0.get(this) != 3) wait()
}
value_0
case 2 =>
synchronized {
while (arfu_0.get(this) != 3) wait()
}
value_0
case 3 => value_0
}
}
[1]} od czerwca 2013 r. ten SIP nie został zatwierdzony. Spodziewam się, że zostanie ona zatwierdzona i włączona do przyszłej wersji Scali na podstawie dyskusji na liście dyskusyjnej. W związku z tym myślę, że mądrze byłoby zwrócić uwagę na obserwację Daniela śpiewaka :
Lazy val jest* nie * Darmowy (a nawet tani). Używaj go tylko wtedy, gdy absolutnie potrzebujesz lenistwa dla poprawności, nie dla optymalizacji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-26 20:05:55
Napisałem post w tej sprawie https://dzone.com/articles/cost-laziness
W skrócie, kara jest tak mała, że w praktyce można ją zignorować.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-11 09:34:23
Biorąc pod uwagę bycode generowany przez scala dla leniwych, może wystąpić problem bezpieczeństwa wątku, jak wspomniano w double check locking http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html?page=1
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-15 09:26:58