@specialized and Arrays

69 views
Skip to first unread message

rklaehn

unread,
Jul 22, 2011, 3:44:03 PM7/22/11
to scala-user
Hi all,

I am using @specialized to save massive amounts of code duplication.
However, it seems that using @specialized with arrays does not produce
what I would have expected.

It seems that while there are "specialized" frontends being generated,
the actual implementation (the little that there is) is not being
specialized.

Is that a fundamental limitation of @specialized, or am I doing
something wrong? I did compile with optimize. If this is indeed a
limitation of @specialized, then I must say that it is pretty severe.
Usually primitive arrays are what you use if you really need high
performance and compact in memory representation.

Here is a very small class to illustrate the problem:

class Test[@specialized(Byte, Short, Int, Long, Float, Double) T] {
def get(a:Array[T], i:Int) = a(i)
}

And here is the byte code:

scala> :javap -c stuff.Test
Compiled from "ArrayOperations.scala"
public class Test extends java.lang.Object implements
scala.ScalaObject{
public static final void main(java.lang.String[]);
Code:
0: getstatic #11; //Field Test$.MODULE$:LTest$;
3: aload_0
4: invokevirtual #13; //Method Test$.main:([Ljava/lang/
String;)V
7: return

public java.lang.Object get(java.lang.Object, int);
Code:
0: getstatic #20; //Field scala/runtime/ScalaRunTime$.MODULE
$:Lscala/runtime/ScalaRunTime$;
3: aload_1
4: iload_2
5: invokevirtual #24; //Method scala/runtime/ScalaRunTime
$.array_apply:(Ljava/lang/Object;I)Ljava/lang/Object;
8: areturn

public byte get$mcB$sp(byte[], int);
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokevirtual #39; //Method get:(Ljava/lang/Object;I)Ljava/
lang/Object;
6: invokestatic #45; //Method scala/runtime/
BoxesRunTime.unboxToByte:(Ljava/lang/Object;)B
9: ireturn

public short get$mcS$sp(short[], int);
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokevirtual #39; //Method get:(Ljava/lang/Object;I)Ljava/
lang/Object;
6: invokestatic #52; //Method scala/runtime/
BoxesRunTime.unboxToShort:(Ljava/lang/Object;)S
9: ireturn

public int get$mcI$sp(int[], int);
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokevirtual #39; //Method get:(Ljava/lang/Object;I)Ljava/
lang/Object;
6: invokestatic #59; //Method scala/runtime/
BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
9: ireturn

public long get$mcJ$sp(long[], int);
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokevirtual #39; //Method get:(Ljava/lang/Object;I)Ljava/
lang/Object;
6: invokestatic #66; //Method scala/runtime/
BoxesRunTime.unboxToLong:(Ljava/lang/Object;)J
9: lreturn

public float get$mcF$sp(float[], int);
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokevirtual #39; //Method get:(Ljava/lang/Object;I)Ljava/
lang/Object;
6: invokestatic #73; //Method scala/runtime/
BoxesRunTime.unboxToFloat:(Ljava/lang/Object;)F
9: freturn

public double get$mcD$sp(double[], int);
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokevirtual #39; //Method get:(Ljava/lang/Object;I)Ljava/
lang/Object;
6: invokestatic #80; //Method scala/runtime/
BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
9: dreturn

public Test();
Code:
0: aload_0
1: invokespecial #87; //Method java/lang/Object."<init>":()V
4: return

}

rklaehn

unread,
Jul 22, 2011, 3:57:53 PM7/22/11
to scala-user
It seems that this is the code of the array_apply method (in
scala.runtime.ScalaRunTime object):

/** Retrieve generic array element */
def array_apply(xs: AnyRef, idx: Int): Any = xs match {
case x: Array[AnyRef] => x(idx).asInstanceOf[Any]
case x: Array[Int] => x(idx).asInstanceOf[Any]
case x: Array[Double] => x(idx).asInstanceOf[Any]
case x: Array[Long] => x(idx).asInstanceOf[Any]
case x: Array[Float] => x(idx).asInstanceOf[Any]
case x: Array[Char] => x(idx).asInstanceOf[Any]
case x: Array[Byte] => x(idx).asInstanceOf[Any]
case x: Array[Short] => x(idx).asInstanceOf[Any]
case x: Array[Boolean] => x(idx).asInstanceOf[Any]
case x: Array[Unit] => x(idx).asInstanceOf[Any]
case null => throw new NullPointerException
}

So there are a total of two box and unbox operations going on for each
array access. One for the index, and one for returning the array
element. I have my doubts that the JVM will find and optimize away
these inefficiencies...

Rex Kerr

unread,
Jul 22, 2011, 4:05:38 PM7/22/11
to rklaehn, scala-user
You can get properly optimized methods if you wrap the array in your test object:

class Test[@specialized(Byte, Short, Int, Long, Float, Double) T](a: Array[T]) {
  def get(i: Int) = a(i)
}

Unfortunately, there are all kinds of other fragilities in specializations, so you may or may not get all the way through your application before giving up and writing the specialized code by hand (or via code generators).

  --Rex

Jason Zaugg

unread,
Jul 22, 2011, 4:09:57 PM7/22/11
to rklaehn, scala-user
On Fri, Jul 22, 2011 at 9:44 PM, rklaehn <rkl...@googlemail.com> wrote:
> Hi all,
>
> I am using @specialized to save massive amounts of code duplication.
> However, it seems that using @specialized with arrays does not produce
> what I would have expected.
>
> It seems that while there are "specialized" frontends being generated,
> the actual implementation (the little that there is) is not being
> specialized.
>
> Is that a fundamental limitation of @specialized, or am I doing
> something wrong? I did compile with optimize. If this is indeed a
> limitation of @specialized, then I must say that it is pretty severe.
> Usually primitive arrays are what you use if you really need high
> performance and compact in memory representation.
>
> Here is a very small class to illustrate the problem:
>
> class Test[@specialized(Byte, Short, Int, Long, Float, Double) T] {
>  def get(a:Array[T], i:Int) = a(i)
> }
>
> And here is the byte code:

The specialized bits go into generated subclasses:

scala> new Test[Int]().getClass
res1: java.lang.Class[_] = class Test$mcI$sp

-jason

rklaehn

unread,
Jul 22, 2011, 4:24:32 PM7/22/11
to scala-user
Phew. Thanks a lot.

I thought that most of the work of the last week would be for naught.

On Jul 22, 10:09 pm, Jason Zaugg <jza...@gmail.com> wrote:

Rex Kerr

unread,
Jul 22, 2011, 4:30:49 PM7/22/11
to Jason Zaugg, rklaehn, scala-user
On Fri, Jul 22, 2011 at 4:09 PM, Jason Zaugg <jza...@gmail.com> wrote:
The specialized bits go into generated subclasses:

scala> new Test[Int]().getClass
res1: java.lang.Class[_] = class Test$mcI$sp

Whoops!  Good catch.  (Or sloppy error on my part.)  It did seem strange that something _that_ simple would be broken.

  --Rex

Reply all
Reply to author
Forward
0 new messages