Как определить бокс / распаковку в Scala - PullRequest
9 голосов
/ 27 июня 2011

Следуя предложению extempore недавно о том, как заставить Scala сообщить мне, происходит ли бокс, посмотрев на байт-код, я создал этот класс:

class X { def foo(ls : Array[Long]) = ls map (_.toDouble)

ИмелПосмотрите на байт-код для foo:

public double[] foo(long[]);
  Code:
   Stack=4, Locals=2, Args_size=2
   0:   getstatic       #11; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   3:   aload_1
   4:   invokevirtual   #16; //Method scala/Predef$.longArrayOps:([J)Lscala/collection/mutable/ArrayOps;
   7:   new     #18; //class X$$anonfun$foo$1
   10:  dup
   11:  aload_0
   12:  invokespecial   #22; //Method X$$anonfun$foo$1."<init>":(LX;)V
   15:  getstatic       #27; //Field scala/Array$.MODULE$:Lscala/Array$;
   18:  getstatic       #32; //Field scala/reflect/Manifest$.MODULE$:Lscala/reflect/Manifest$;
   21:  invokevirtual   #36; //Method scala/reflect/Manifest$.Double:()Lscala/reflect/AnyValManifest;
   24:  invokevirtual   #40; //Method scala/Array$.canBuildFrom:(Lscala/reflect/ClassManifest;)Lscala/collection/generic/CanBuildFrom;
   27:  invokeinterface #46,  3; //InterfaceMethod scala/collection/TraversableLike.map:(Lscala/Function1;Lscala/collection/generic/CanBuildFrom;)Ljava/lan                                   g/Object;
   32:  checkcast       #48; //class "[D"
   35:  areturn
  LineNumberTable:
   line 7: 0

Нет никаких признаков коробки / распаковки там.Но я все еще подозреваю, поэтому я скомпилировал его с помощью -print ():

[[syntax trees at end of cleanup]]// Scala source: X.scala
package <empty> {
  class X extends java.lang.Object with ScalaObject {
    def foo(ls: Array[Long]): Array[Double] = scala.this.Predef.longArrayOps(ls).map({
(new anonymous class X$$anonfun$foo$1(X.this): Function1)
}, scala.this.Array.canBuildFrom(reflect.this.Manifest.Double())).$asInstanceOf[Array[Double]]();
  def this(): X = {
    X.super.this();
    ()
  }
};
@SerialVersionUID(0) final <synthetic> class X$$anonfun$foo$1 extends scala.runtime.AbstractFunction1$mcDJ$sp with Serializable {
  final def apply(x$1: Long): Double = X$$anonfun$foo$1.this.apply$mcDJ$sp(x$1);
  <specialized> def apply$mcDJ$sp(v1: Long): Double = v1.toDouble();
  final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Double.box(X$$anonfun$foo$1.this.apply(scala.Long.unbox(v1)));
    def this($outer: X): anonymous class X$$anonfun$foo$1 = {
      X$$anonfun$foo$1.super.this();
      ()
    }
  }
}

Основные наблюдения этого кода заключаются в том, что созданная анонимная функция была специализирована для Long => Double и что map функциональность обеспечивается longArrayOps(ls).map (ArrayOps является не специализированным ).

Вопрос : "происходит ли в этом примере упаковка / распаковка?«

Ответы [ 2 ]

6 голосов
/ 27 июня 2011

Там, вероятно, происходит бокс. Вы должны посмотреть на позицию, в которой сделан фактический map -звук:

27:  invokeinterface #46,  3; //InterfaceMethod scala/collection/TraversableLike.map:(Lscala/Function1;Lscala/collection/generic/CanBuildFrom;)Ljava/lang/Object;

Во время выполнения это должно перейти к ArrayOps#ofLong. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 100 * * не будет специализированным, маловероятно, что ваш map -тзыв пройдет без бокса.

Чтобы узнать наверняка, вы можете либо попытаться проследить за вызовами через байт-код (это может быть сложно, так как в вашей функции вы можете столкнуться с диспетчеризацией во время выполнения), либо пройти через отладчик (сложно, потому что большинство отладчиков не показывают байт-код, однако вы можете установить контрольные точки для методов бокса в Scala или Java).

По практическим причинам мы обнаружили, что это может быть неуместно, если бокс выполняется в байт-коде, потому что для горячих методов компилятору Hotspot иногда удается полностью встроить стандартную цепочку вызовов функций более высокого порядка, в этом случае бокс может быть исключен.

2 голосов
/ 27 июня 2011

Вы можете использовать профилировщик (jvisualvm поставляется бесплатно с Oracle SDK) для поиска упаковки / распаковки.Преимущество заключается в том, что вы будете замечать только релевантный бокс - ничего такого, что случается всего несколько раз, и ничего, что не может быть оптимизировано HotSpot.

Ну, я не совсем уверен насчет последнего, но я ожидаю, что более вероятно, что JIT все еще используется при использовании профилировщика выборки (который также предоставляет jvisualvm).

...