2011-02-13 12 views
8

En la mejor respuesta aceptada al this question, hay una explicación clara de por qué ocurre el boxeo.¿Cómo se reconoce el boxeo/desempaquetado en un código Scala descompuesto?

Sin embargo, si descompilo el código (usando el descompilador de Java) no puedo ver el uso de scala.runtime.BoxesRunTime. Además, si perfilo el código (usando JProfiler) no puedo ver ninguna instancia de BoxesRunTime.

Entonces, ¿cómo realmente veo una prueba de boxeo/unboxing?

Respuesta

7

En este código:

class Foo[T] { 
    def bar(i: T) = i 
} 


object Main { 
    def main(args: Array[String]) { 
    val f = new Foo[Int] 
    f.bar(5) 
    } 
} 

La invocación de bar debería caja primero el número entero. Compilar con Scala y 2.8.1 usando:

javap -c -l -private -verbose -classpath <dir> Main$ 

para ver el código de bytes producido por el método main de los rendimientos Main clase:

public void main(java.lang.String[]);              
...                     
    9: iconst_5                       
    10: invokestatic #24; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;  
    13: invokevirtual #28; //Method Foo.bar:(Ljava/lang/Object;)Ljava/lang/Object;   
    16: pop                        
    17: return                      
... 

Se puede ver la llamada a BoxesRunTime antes de la llamada a bar .

BoxesRunTime es un objeto que contiene métodos de boxeo para tipos primitivos, por lo que debe haber exactamente una instancia en total. El truco aquí es que este archivo particular en la biblioteca fue escrito en Java, y las conversiones son métodos estáticos. Por esta razón, no hay instancias de él en tiempo de ejecución, aunque usarlo en el código de Scala se siente como si fuera un objeto.

Probablemente deberías buscar primitivos en caja (por ejemplo, java.lang.Integer) con JProfile, aunque no estoy seguro de cómo funciona la JVM y si realmente puede reescribir el código en tiempo de ejecución y optimizarlo para evitar el boxeo. Que yo sepa, no se debe aplicar la especialización (pero creo que CLR lo hace). Algunas microbenchmarks con y sin la situación del boxeo son otra forma de descubrir qué sucede en el tiempo de ejecución.

EDIT:

Lo anterior está asumiendo que un parámetro de tipo no fue anotado con la @specialized anotación. En este caso, se puede evitar el boxeo/unboxing. Ciertas clases en la biblioteca estándar están especializadas. Ver this sid.

1

Dado el siguiente programa Test.scala:

object Test { 
    def main(args:Array[String]) { 
    val list = List(1,5,15) 
    val res = list.map(e => e*2).filter(e => e>10) 
    } 
} 

Si compilo con scalac -Xprint:jvm Test.scala, me sale este fragmento lo que sugiere que la especialización se produce (lo siento por amplia pegar):

package <empty> { 
    final class Test extends java.lang.Object with ScalaObject { 
    def main(args: Array[java.lang.String]): Unit = { 
     val list: List = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 5, 15})); 
     val res: List = list.map({ 
     (new Test$$anonfun$1(): Function1) 
     }, immutable.this.List.canBuildFrom()).$asInstanceOf[scala.collection.TraversableLike]().filter({ 
     (new Test$$anonfun$2(): Function1) 
     }).$asInstanceOf[List](); 
    () 
    }; 
    def this(): object Test = { 
     Test.super.this(); 
    () 
    } 
    }; 
    @SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp { 
    final def apply(e: Int): Int = Test$$anonfun$1.this.apply$mcII$sp(e); 
    <specialized> def apply$mcII$sp(v1: Int): Int = v1.*(2); 
    final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Int.box(Test$$anonfun$1.this.apply(scala.Int.unbox(v1))); 
    def this(): Test$$anonfun$1 = { 
     Test$$anonfun$1.super.this(); 
    () 
    } 
    }; 
    @SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$2 extends scala.runtime.AbstractFunction1$mcZI$sp { 
    final def apply(e: Int): Boolean = Test$$anonfun$2.this.apply$mcZI$sp(e); 
    <specialized> def apply$mcZI$sp(v1: Int): Boolean = v1.>(10); 
    final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Boolean.box(Test$$anonfun$2.this.apply(scala.Int.unbox(v1))); 
    def this(): Test$$anonfun$2 = { 
     Test$$anonfun$2.super.this(); 
    () 
    } 
    } 
} 

podría ser la razón no ve ninguna evidencia de boxeo en bytecode ...

+1

La especialización se produce para la función anonymous classes 'Test $$ anonfun $ #', ya que las funciones son especializadas. Sin embargo, estas clases anónimas de función se usan dentro de los métodos 'map' y' filter' que no están especializados y llaman a su 'genérico' apply' predeterminado, que espera un objeto - el boxeo debería ocurrir allí. Si descompilas el código de 'TraversableLike', y buscas' map' o 'filter', deberían tener invocaciones a' BoxesRunTime'. – axel22

Cuestiones relacionadas