2011-11-19 23 views
8

Perché questo codice Scala:Perché Scala costruisce una nuova tupla quando spacchetta una tupla?

class Test 
{ 
    def foo: (Int, String) = 
    { 
    (123, "123") 
    } 

    def bar: Unit = 
    { 
    val (i, s) = foo 
    } 
} 

generare i seguenti bytecode per bar() che costruisce una nuova Tuple2, passa il Tuple2 da foo() ad esso e poi ottiene i valori fuori di esso?

public void bar(); 
Code: 
0: aload_0 
1: invokevirtual #28; //Method foo:()Lscala/Tuple2; 
4: astore_2 
5: aload_2 
6: ifnull 40 
9: new  #7; //class scala/Tuple2 
12: dup 
13: aload_2 
14: invokevirtual #32; //Method scala/Tuple2._1:()Ljava/lang/Object; 
17: aload_2 
18: invokevirtual #35; //Method scala/Tuple2._2:()Ljava/lang/Object; 
21: invokespecial #20; //Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V 
24: astore_1 
25: aload_1 
26: invokevirtual #39; //Method scala/Tuple2._1$mcI$sp:()I 
29: istore_3 
30: aload_1 
31: invokevirtual #35; //Method scala/Tuple2._2:()Ljava/lang/Object; 
34: checkcast  #41; //class java/lang/String 
37: astore 4 

È questo perché il compilatore non sta controllando che foo() s valore di ritorno non è una tupla?

La JVM ottimizzerà comunque la costruzione?

+0

Il valore di ritorno di 'pippo' è una tupla. Perché pensi che non lo sia? –

+0

foo restituisce un tipo, ma a 9: in bar() costruisce una nuova tupla. –

risposta

4

Questo sembra essere secondo i spec (in 4.1 Rapporto Dichiarazioni e definizioni - poco riformattato per StackOverflow display):

Le definizioni di valore possono alternativamente avere un modello (§8.1) come lato sinistro. Se p è qualche modello diverso da un semplice nome o un nome seguito da due punti e un tipo, quindi la definizione valore val p = e si espande come segue:

variabili
  1. se il pattern p è legato x1, . . . , xn, dove n >= 1: Qui, $x è un nome nuovo.
val $x = e match {case p => (x1, . . . , xn)} 
    val x1 = $x._1 
    . . . 
    val xn = $x._n 

Quindi la creazione tupla accade nella fase di parser. Così val (i, s) = (1, "s") spende al termine della fase di parser a:

private[this] val x$1 = scala.Tuple2(1, "s"): @scala.unchecked match {  
    case scala.Tuple2((i @ _), (s @ _)) => scala.Tuple2(i, s) 
}; 
val i = x$1._1; 
val s = x$1._2 

misura questo su questo semplice test su un milione di iterazioni:

def foo: (Int, String) = (123, "123") 
def bar: Unit = { val (i, s) = foo } 
def bam: Unit = { val f = foo; val i = f._1; val s = f._2 } 

rendimenti

foo: Elapsed: 0.030 
bar: Elapsed: 0.051 
._1 ._2 access: Elapsed: 0.040 

e con la bandierina -ottimizzazione :

foo: Elapsed: 0.027 
bar: Elapsed: 0.049 
._1 ._2 access: Elapsed: 0.029 
+0

Grazie. BTW, il compilatore di Scala 2.9 con e senza -optimize genera lo stesso bytecode per la classe di test che ho incollato, ma forse ha aiutato il codice di benchmarking a rendere i tempi più veloci. –

4

Sembra un'eccellente opportunità di ottimizzazione in scalac.

La parte rilevante del compilatore noi Unapplies#caseClassUnapplyReturnValue, che chiama TreeDSL#SOME per generare il codice per creare una nuova TupleN

Problemi correlati