3

When profiling my application, I can see boxing of Double values done in the Array[Double] constructor, like this (copied from JMC / JFR):

Double Double.valueOf(double)   1472
   Double BoxesRunTime.boxToDouble(double)  1472
      Object WrappedArray$ofDouble.apply(int)   1468
         Object IndexedSeqLike$Elements.next()  1365
            void Iterator.foreach(Function1)    1365
               void Iterator.foreach$(Iterator, Function1)  1365
                  void AbstractIterator.foreach(Function1)  1365
                     Object Array$.apply(Seq, ClassTag) 1365

When I check the Array constructor code, there is the following comment (see Array.scala#L182-L192):

  // Subject to a compiler optimization in Cleanup.
  // Array(e0, ..., en) is translated to { val a = new Array(3); a(i) = ei; a }

Such translation sounds like a reasonable thing to do. Why am I still seeing the Iterator / boxToDouble in my callstacks? Is the comment somehow obsolete, or do I need some specific compiler settings for this optimization to be applied?

The code calling the array constructor is a class member initialization:

  var elements = Array[Double](
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
  )

I have tested this with Scala 2.12.12 and 2.13.3

The comment was added in a commit Optimize primitive Array(e1, ..., en) (in Scala 2.11.0-M1).

1 Answer 1

4

It seems the optimization is quite fragile. Changing the types and literals involved causes the optimization to be done (or not).

var el = Array[Double](1)

var el = Array[Double](1.0)

Both produce the ugly code:

       0: aload_0
       1: invokespecial #27                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: getstatic     #33                 // Field scala/Array$.MODULE$:Lscala/Array$;
       8: getstatic     #38                 // Field scala/runtime/ScalaRunTime$.MODULE$:Lscala/runtime/ScalaRunTime$;
      11: iconst_1
      12: newarray       double
      14: dup
      15: iconst_0
      16: dconst_1
      17: dastore
      18: invokevirtual #42                 // Method scala/runtime/ScalaRunTime$.wrapDoubleArray:([D)Lscala/collection/immutable/ArraySeq;
      21: getstatic     #47                 // Field scala/reflect/ClassTag$.MODULE$:Lscala/reflect/ClassTag$;
      24: invokevirtual #51                 // Method scala/reflect/ClassTag$.Double:()Lscala/reflect/ManifestFactory$DoubleManifest;
      27: invokevirtual #55                 // Method scala/Array$.apply:(Lscala/collection/immutable/Seq;Lscala/reflect/ClassTag;)Ljava/lang/Object;
      30: checkcast     #56                 // class "[D"
      33: putfield      #18                 // Field el:[D
      36: return
}

While

var el = Array(1.0)

var el: Array[Double] = Array(1)

produced beautiful result:

       0: aload_0
       1: invokespecial #22                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_1
       6: newarray       double
       8: dup
       9: iconst_0
      10: dconst_1
      11: dastore
      12: putfield      #13                 // Field el:[D
      15: return

I have tried Dotty as well and it seems to apply the optimization for all variants.

Posted as scala/bug#12201.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.