- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Can I get an optimisation?
Sun, 2010-08-29, 17:51
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.
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.
Tue, 2010-08-31, 00:17
#2
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 }
...
Tue, 2010-08-31, 00:37
#3
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.
Tue, 2010-08-31, 01:37
#4
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:
- 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.
-0xe1a
On Sun, Aug 29, 2010 at 9:52 AM, Josh Suereth <joshua.suereth@gmail.com> wrote: