2016-02-24 10 views
11

Ecco uno strano comportamento in cui sono caduto e non riesco a trovare alcun suggerimento sul perché sia ​​così. Io uso in questo esempio il estimate method of SizeEstimator from Spark, ma non ho trovato alcun problema tecnico nel loro codice quindi mi chiedo perché - se prevedono una buona stima della memoria - il motivo per cui ho questo:Scala: perché Double consuma meno memoria di Floats in questo caso?

stampe uscita
val buf1 = new ArrayBuffer[(Int,Double)] 
var i = 0 
while (i < 3) { 
    buf1 += ((i,i.toDouble)) 
    i += 1 
} 
System.out.println(s"Raw size with doubles: ${SizeEstimator.estimate(buf1)}") 
val ite1 = buf1.toIterator 
var size1: Long = 0l 
while (ite1.hasNext) { 
    val cur = ite1.next() 
    size1 += SizeEstimator.estimate(cur) 
} 
System.out.println(s"Size with doubles: $size1") 

val buf2 = new ArrayBuffer[(Int,Float)] 
i = 0 
while (i < 3) { 
    buf2 += ((i,i.toFloat)) 
    i += 1 
} 
System.out.println(s"Raw size with floats: ${SizeEstimator.estimate(buf2)}") 
val ite2 = buf2.toIterator 
var size2: Long = 0l 
while (ite2.hasNext) { 
    val cur = ite2.next() 
    size2 += SizeEstimator.estimate(cur) 
} 
System.out.println(s"Size with floats: $size2") 

la console:

Raw size with doubles: 200 
Size with doubles: 96 
Raw size with floats: 272 
Size with floats: 168 

Quindi la mia domanda è piuttosto ingenua: perché i float tendono a prendere più memoria che doppio in questo caso? E perché diventa ancora peggio quando lo trasformo in un iteratore (primo caso, c'è un rapporto del 75% che diventa un rapporto del 50% quando si trasforma in un iteratore!).

(Per avere più contesto, caddi in questo quando si cerca di un'applicazione Spark "ottimizzare" modificando Double a Float e scoperto che in realtà ha più memoria avendo carri che raddoppia ...)

PS : non è a causa delle piccole dimensioni del buffer (qui 3), se ho messo 100 invece ottengo:

Raw size with doubles: 3752 
Size with doubles: 3200 
Raw size with floats: 6152 
Size with floats: 5600 

e galleggia ancora consumano più memoria ... Ma il rapporto si sono stabilizzati, così sembra che il rapporti diversi nella trasformazione in iteratore devono essere dovuti a un sovraccarico che immagino.

EDIT: Sembra che Product2 è in realtà specializzata solo su Int, Long e Double:

trait Product2[@specialized(Int, Long, Double) +T1, @specialized(Int, Long, Double) +T2] extends Any with Product 

fare chiunque so perché Float non viene presa in considerazione? Né Short che porta a comportamenti strani ...

+0

dispiace I didn vedere l'aggiornamento prima di pubblicare l'anwer. Se lo desideri, posso cancellare la risposta – Odomontois

+0

Nessuna risposta è buona perché hai fornito un link che spiegava perché non è specializzato su tutti i primitivi! È dovuto al numero combinatorio che potrebbe portare ... che in realtà ha senso =) È bello sapere prima di provare a ottimizzare stupidamente come ho provato! –

risposta

13

Questo perché Tuple2 è @specialized per Double ma non specializzati per Float.

Ciò significa (Int,Double) sarà presentato come struttura con 2 campi di tipi Java primitivi int e double, mentre (Int,Float) saranno presentati come struttura di tipo int e involucro java.lang.Float campi

Più discussione here

+0

C'è qualcosa di strano nel tuo link, dicono che è perché non vogliono avere troppe specializzazioni. Ma quando guardi il codice, Product3 non è nemmeno specializzato ... Quindi sono solo Product1 e Prodcut2 ... Potrebbero facilmente aver aggiunto qualche specializzazione sui soliti tipi come float e shorts! –

+1

@ Vince.Bdn 'Tuple2' è usato molto spesso di' Tuple3'. Quindi immagino che abbiano deciso che ulteriori definizioni non valgono la dimensione del barattolo della libreria. Puoi semplicemente usare case class per un negozio efficace, [miniboxing] (http://scala-miniboxing.org) per un accesso efficiente e [shapeless] (https://github.com/milessabin/shapeless) per trasformazioni generiche efficienti – Odomontois

Problemi correlati