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

Can I get an optimisation?

4 replies
Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
import annotation.switch

object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}

import WeekDay._

class Test {
  def couldThisBeOptimised(x : WeekDay) = x match {
    case Mon => "Case of the Mondays"
    case Tue => "Ruby Tuesdays?"
    case Fri => "Thank God!"
    case _ => "Not an interesting day"
  }
}

Just for fun, I tried to see if I could force this by hand:
object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
  val Mon_ID = Mon.id;
  val Tue_ID = Tue.id;
  val Wed_ID = Wed.id;
  val Thu_ID = Thu.id;
  val Fri_ID = Fri.id;
  val Sat_ID = Sat.id;
  val Sun_ID = Sun.id;
}
import WeekDay._
scala> def notOptimisedEnum(x : WeekDay) = (x.id : @switch) match {
     |     case Mon_ID => "Case of the Mondays"
     |     case Tue_ID => "Ruby Tuesdays?"
     |     case Fri_ID => "Thank God!"
     |     case _ => "Not an interesting day"
     |   }
<console>:13: error: could not emit switch for @switch annotated match
       def notOptimisedEnum(x : WeekDay) = (x.id : @switch) match {

So, my question is:  How relevant is emitting a tableswitch bytecode when pattern matching an Enumeration?   This seems like a chalk against Scala enumerations (and for encoding enumerations inside Java).   However, I haven't ever had a tight loop where I had to rely on the tableswitch bytecode for performance before, so I was wondering what everyone else thought here.

Alex Cruise
Joined: 2008-12-17,
User offline. Last seen 2 years 26 weeks ago.
Re: Can I get an optimisation?
javac only emits a switch opcode when the switch statement is in the same compilation unit (i.e. file) as the enum is declared.  Otherwise you get an if-then-else ladder.
-0xe1a

On Sun, Aug 29, 2010 at 9:52 AM, Josh Suereth <joshua.suereth@gmail.com> wrote:
import annotation.switch

object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}

import WeekDay._

class Test {
  def couldThisBeOptimised(x : WeekDay) = x match {
    case Mon => "Case of the Mondays"
    case Tue => "Ruby Tuesdays?"
    case Fri => "Thank God!"
    case _ => "Not an interesting day"
  }
}

Just for fun, I tried to see if I could force this by hand:
object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
  val Mon_ID = Mon.id;
  val Tue_ID = Tue.id;
  val Wed_ID = Wed.id;
  val Thu_ID = Thu.id;
  val Fri_ID = Fri.id;
  val Sat_ID = Sat.id;
  val Sun_ID = Sun.id;
}
import WeekDay._
scala> def notOptimisedEnum(x : WeekDay) = (x.id : @switch) match {
     |     case Mon_ID => "Case of the Mondays"
     |     case Tue_ID => "Ruby Tuesdays?"
     |     case Fri_ID => "Thank God!"
     |     case _ => "Not an interesting day"
     |   }
<console>:13: error: could not emit switch for @switch annotated match
       def notOptimisedEnum(x : WeekDay) = (x.id : @switch) match {

So, my question is:  How relevant is emitting a tableswitch bytecode when pattern matching an Enumeration?   This seems like a chalk against Scala enumerations (and for encoding enumerations inside Java).   However, I haven't ever had a tight loop where I had to rely on the tableswitch bytecode for performance before, so I was wondering what everyone else thought here.


Michael Bayne
Joined: 2010-08-31,
User offline. Last seen 42 years 45 weeks ago.
Re: Can I get an optimisation?

On Sun, Aug 29, 2010 at 10:11 AM, Alex Cruise wrote:
> javac only emits a switch opcode when the switch statement is in the same
> compilation unit (i.e. file) as the enum is declared.  Otherwise you get an
> if-then-else ladder.

You sure about that?

Foo.java:
public enum Foo { A, B, C };

Test.java:
class Test {
public int test () {
Foo foo = Foo.A;
switch (foo) {
case A: return 1;
case B: return 2;
case C: return 3;
default: return 0;
}
}
}

% javac Foo.java
% javac Test.java
% javap -c Test
...
8: invokevirtual #4; //Method Foo.ordinal:()I
11: iaload
12: tableswitch{ //1 to 3
1: 40;
2: 42;
3: 44;
default: 46 }
...

Michael Bayne
Joined: 2010-08-31,
User offline. Last seen 42 years 45 weeks ago.
Re: Can I get an optimisation?
On Mon, Aug 30, 2010 at 4:10 PM, Michael Bayne wrote: > On Sun, Aug 29, 2010 at 10:11 AM, Alex Cruise wrote: >> javac only emits a switch opcode when the switch statement is in the same >> compilation unit (i.e. file) as the enum is declared.  Otherwise you get an >> if-then-else ladder. > > You sure about that? Hrm. Actually it looks more complicated. The compiler seems to emit an auxiliary table for each use site. javac.comp.Lower handily explains: *

For each enum that appears as the type of a switch * expression, we maintain an EnumMapping to assist in the * translation, as exemplified by the following example: * *

we translate *

    *          switch(colorExpression) {
    *          case red: stmt1;
    *          case green: stmt2;
    *          }
    *  
* into *
    *          switch(Outer$0.$EnumMap$Color[colorExpression.ordinal()]) {
    *          case 1: stmt1;
    *          case 2: stmt2
    *          }
    *  
* with the auxiliary table initialized as follows: *
    *          class Outer$0 {
    *              synthetic final int[] $EnumMap$Color = new
int[Color.values().length];
    *              static {
    *                  try { $EnumMap$Color[red.ordinal()] = 1; }
catch (NoSuchFieldError ex) {}
    *                  try { $EnumMap$Color[green.ordinal()] = 2; }
catch (NoSuchFieldError ex) {}
    *              }
    *          }
    *  
* class EnumMapping provides mapping data and support methods for this translation. That would explain why my attempt to break things by recompiling the enum without recompiling the switching code were thwarted.

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: Can I get an optimisation?
Wow... so you could potentially translate that optimisation to scala's enumeration if we ever cared.   Seems like a fun idea for a plugin perhaps.

- Josh

On Mon, Aug 30, 2010 at 7:28 PM, Michael Bayne <mdb@samskivert.com> wrote:
On Mon, Aug 30, 2010 at 4:10 PM, Michael Bayne <mdb@samskivert.com> wrote:
> On Sun, Aug 29, 2010 at 10:11 AM, Alex Cruise <alex@cluonflux.com> wrote:
>> javac only emits a switch opcode when the switch statement is in the same
>> compilation unit (i.e. file) as the enum is declared.  Otherwise you get an
>> if-then-else ladder.
>
> You sure about that?

Hrm. Actually it looks more complicated. The compiler seems to emit an
auxiliary table for each use site. javac.comp.Lower handily explains:

   *  <p>For each enum that appears as the type of a switch
   *  expression, we maintain an EnumMapping to assist in the
   *  translation, as exemplified by the following example:
   *
   *  <p>we translate
   *  <pre>
   *          switch(colorExpression) {
   *          case red: stmt1;
   *          case green: stmt2;
   *          }
   *  </pre>
   *  into
   *  <pre>
   *          switch(Outer$0.$EnumMap$Color[colorExpression.ordinal()]) {
   *          case 1: stmt1;
   *          case 2: stmt2
   *          }
   *  </pre>
   *  with the auxiliary table initialized as follows:
   *  <pre>
   *          class Outer$0 {
   *              synthetic final int[] $EnumMap$Color = new
int[Color.values().length];
   *              static {
   *                  try { $EnumMap$Color[red.ordinal()] = 1; }
catch (NoSuchFieldError ex) {}
   *                  try { $EnumMap$Color[green.ordinal()] = 2; }
catch (NoSuchFieldError ex) {}
   *              }
   *          }
   *  </pre>
   *  class EnumMapping provides mapping data and support methods
for this translation.

That would explain why my attempt to break things by recompiling the
enum without recompiling the switching code were thwarted.

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