This page is no longer maintained — Please continue to the home page at www.scala-lang.org

@specialized and Arrays

5 replies
Rüdiger Klaehn
Joined: 2009-06-02,
User offline. Last seen 42 years 45 weeks ago.

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."":()V
4: return

}

Rüdiger Klaehn
Joined: 2009-06-02,
User offline. Last seen 42 years 45 weeks ago.
Re: @specialized and Arrays

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...

On Jul 22, 9:44 pm, rklaehn 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:
>
> 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."":()V
>    4:   return
>
>
>
>
>
>
>
> }

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: @specialized and Arrays
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


On Fri, Jul 22, 2011 at 3:44 PM, rklaehn <rklaehn [at] googlemail [dot] 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:

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

}

Jason Zaugg
Joined: 2009-05-18,
User offline. Last seen 38 weeks 5 days ago.
Re: @specialized and Arrays

On Fri, Jul 22, 2011 at 9:44 PM, rklaehn 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

Rüdiger Klaehn
Joined: 2009-06-02,
User offline. Last seen 42 years 45 weeks ago.
Re: @specialized and Arrays

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 wrote:
> On Fri, Jul 22, 2011 at 9:44 PM, rklaehn 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

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: @specialized and Arrays
On Fri, Jul 22, 2011 at 4:09 PM, Jason Zaugg <jzaugg [at] gmail [dot] 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

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland