2010-10-07 5 views
7

ho incontrato il seguente codice nel numero speciale Scala di JAXMag:L'uso della val pigra per la cache di rappresentazione di stringa

package com.weiglewilczek.gameoflife 

case class Cell(x: Int, y: Int) { 
    override def toString = position 
    private lazy val position = "(%s, %s)".format(x, y) 
} 

se il suo utilizzo lazy val nel codice sopra forniscono considerevolmente più prestazioni rispetto il seguente codice?

package com.weiglewilczek.gameoflife 

case class Cell(x: Int, y: Int) { 
    override def toString = "(%s, %s)".format(x, y) 
} 

O è solo un caso di ottimizzazione non necessaria?

+0

pigro dovrebbe spingere il calcolo del valore fino a quando non viene chiamato. Comunque, bella domanda. Crea un oggetto per mantenere la funzione per la valutazione? – wheaties

+2

Solo per quello che sai, puoi ottenere lo stesso formato di stringa chiamando '(x, y) .toString' –

risposta

0

Le classi di casi sono, per definizione, immutabili. Anche qualsiasi valore restituito da toString sarà anch'esso immutabile. Quindi ha senso essenzialmente "memorizzare" questo valore utilizzando un valore lazy val. D'altra parte, l'implementazione toString fornita fa poco più del toString predefinito fornito da tutte le classi case. Non sarei sorpreso se una classe di case vaniglia toString usasse una val pigra sotto.

+3

Immutable per definizione? scala> case class Foo (var x: Int) definita classe Foo –

+0

Non sapevo che il modificatore 'var' potesse essere usato sui membri dei dati della classe case. Avrei dovuto dire, "la classe di caso data nella domanda è immutabile per definizione" anche se continuo a pensare che quello che hai fatto sia puro male. –

1

Nel primo frammento position verrà calcolato solo una volta, su richiesta, quando viene chiamato il metodo toString. Nel secondo frammento, il corpo toString verrà rivalutato ogni volta che viene chiamato il metodo. Dato che x e non possono essere modificati, non ha senso e il valore toString deve essere memorizzato.

0

Mi sembra una micro-ottimizzazione. JVM è in grado di prendersi cura di questi casi.

+6

Non c'è assolutamente nulla nella JVM che memorizzerà i risultati del metodo come questo al di fuori dell'ambito locale (cioè, se si chiama .toString due volte nello stesso metodo, la JVM ha la possibilità di memorizzare i ritorni del metodo semplice nella cache.) Memorizzare costantemente tali risultati è al di fuori del suo ambito, il che è positivo dato che (in questo caso) la memoizzazione automatica comporterebbe un significativo ingombro della memoria –

19

Una cosa da notare su lazy vals è che, mentre vengono calcolati solo una volta, ogni accesso ad essi è protetto da un wrapper con blocco a doppia verifica. Ciò è necessario per impedire a due thread diversi di tentare di inizializzare il valore allo stesso tempo con risultati esilaranti. Ora il blocco a doppia verifica è abbastanza efficiente (ora che funziona effettivamente nella JVM) e nella maggior parte dei casi non richiede l'acquisizione del blocco, ma c'è un sovraccarico maggiore rispetto a un semplice accesso ai valori.

In aggiunta (e in qualche modo ovviamente), memorizzando nella cache la rappresentazione di stringa del proprio oggetto, si rinuncia esplicitamente ai cicli della CPU per eventuali aumenti di memoria di grandi dimensioni. Le stringhe nella versione "def" possono essere raccolte tramite garbage collection, mentre quelle nella versione "lazy val" non lo saranno.

Infine, come sempre accade con le domande sulle prestazioni, le ipotesi basate sulla teoria non significano quasi nulla senza un benchmark basato sui fatti. Non lo saprai mai senza una profilazione, quindi potresti anche provarlo e vedere.

13

toString può essere sostituito direttamente da lazy val.

scala> case class Cell(x: Int, y: Int) { 
    | override lazy val toString = {println("here"); "(%s, %s)".format(x, y)} 
    | } 
defined class Cell 

scala> {val c = Cell(1, 2); (c.toString, c.toString)} 
here 
res0: (String, String) = ((1, 2),(1, 2)) 

Nota che un def non può ignorare un val - si può solo fare i membri più stabile nella classe sub.

+4

+1, non si sa che 'def' può essere sovrascritto da un' lazy val '. :) – missingfaktor

Problemi correlati