开发者

How to spot boxing/unboxing in Scala

Following a suggestion by extempore recently about how to get scala to tell me whether there was boxing going on by looking at the bytecode, I created this class:

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

Had a look at the bytecode for 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开发者_如何学C:()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

No signs of box/unbox there. But I'm still suspicious, so I compiled it with -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();
      ()
    }
  }
}

The main observations about this code are that the created anonymous function has been specialized for Long => Double and that the map functionality is provided by longArrayOps(ls).map (ArrayOps is not specialized).

The question is: "is boxing/unboxing occurring in this example?"


There probably is boxing going on. You have to look at position where the actual map-call is made:

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

At runtime this should go to ArrayOps#ofLong. Since ArrayOps is not specialized it's unlikely that your map-call get's through without boxing.

To find out for sure you can either try to follow the calls through the bytecode (may be difficult because as in your function you may encounter runtime dispatch) or step through with a debugger (difficult because most debuggers don't show the bytecode, however you may be able to set breakpoints to Scala's or Java's boxing methods).

For practical reasons we found that it may be irrelevant if boxing is done in bytecode because for hot methods the Hotspot compiler sometimes manages to completely inline the standard higher order function call chain in which case boxing can be eliminated.


You can use a profiler (jvisualvm comes free with Oracle SDK) to look for boxing/unboxing. The advantage is that you will only spot relevant boxing - nothing that happens only a few times and nothing that gets optimized away by HotSpot.

Well, I'm not totally sure about the last one but I expect it to be more probably that JIT is still used when using a sampling profiler (which jvisualvm also provides).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜