Une fonctionnalité pratique de Scala est lazy val
que l'évaluation d'un val
est retardée jusqu'à ce qu'elle soit nécessaire (au premier accès).
Bien sûr, cela lazy val
doit avoir une surcharge - quelque part, Scala doit savoir si la valeur a déjà été évaluée et l'évaluation doit être synchronisée, car plusieurs threads peuvent essayer d'accéder à la valeur pour la première fois en même temps.
Quel est exactement le coût d'un lazy val
- y a-t-il un indicateur booléen caché associé à a lazy val
pour suivre s'il a été évalué ou non, qu'est-ce qui est exactement synchronisé et y a-t-il d'autres coûts?
De plus, supposons que je fasse ceci:
class Something {
lazy val (x, y) = { ... }
}
Est-ce la même chose que d'avoir deux lazy val
s séparés x
et y
ou est-ce que je reçois la surcharge une seule fois, pour la paire (x, y)
?
la source
bitmap$0
champ est volatile dans l'implémentation actuelle (2.8).Il semble que le compilateur organise un champ int bitmap au niveau de la classe pour marquer plusieurs champs paresseux comme initialisés (ou non) et initialise le champ cible dans un bloc synchronisé si le xor pertinent du bitmap indique qu'il est nécessaire.
En utilisant:
produit un échantillon de bytecode:
Les valeurs paraphées dans les tuples comme
lazy val (x,y) = { ... }
ont une mise en cache imbriquée via le même mécanisme. Le résultat du tuple est évalué et mis en cache paresseusement, et un accès de x ou de y déclenchera l'évaluation du tuple. L'extraction de la valeur individuelle du tuple est effectuée indépendamment et paresseusement (et mise en cache). Ainsi , le code ci - dessus double instanciation génère unx
,y
et unx$1
champ de typeTuple2
.la source
Avec Scala 2.10, une valeur paresseuse comme:
est compilé en code d'octet qui ressemble au code Java suivant:
Notez que le bitmap est représenté par un
boolean
. Si vous ajoutez un autre champ, le compilateur augmentera la taille du champ pour pouvoir représenter au moins 2 valeurs, c'est-à-dire sous forme debyte
. Cela continue juste pour les classes énormes.Mais vous vous demandez peut-être pourquoi cela fonctionne? Les caches locaux de thread doivent être effacés lors de la saisie d'un bloc synchronisé de sorte que la
x
valeur non volatile soit vidée en mémoire. Cet article de blog donne une explication .la source
Scala SIP-20 propose une nouvelle implémentation de lazy val, qui est plus correcte mais ~ 25% plus lente que la version "actuelle".
L' implémentation proposée ressemble à ceci:
En juin 2013, ce SIP n'a pas été approuvé. Je pense qu'il sera probablement approuvé et inclus dans une future version de Scala basée sur la discussion de la liste de diffusion. Par conséquent, je pense qu'il serait sage de tenir compte de l'observation de Daniel Spiewak :
la source
J'ai écrit un article concernant ce problème https://dzone.com/articles/cost-laziness
En bref, la pénalité est si faible qu'en pratique, vous pouvez l'ignorer.
la source
étant donné le bycode généré par scala pour lazy, il peut souffrir d'un problème de sécurité des threads comme mentionné dans le verrouillage de double vérification http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html?page=1
la source