devoured elysium
devoured elysium

Reputation: 105057

Performance of Java Enums?

I am implementing a 2-player game that will be run in a tight loop literally hundreds of thousands of times, being then performance paramount.

My code actually looks something like this:

public class Table {
    private final int WHITE_PLAYER = +1;
    private final int BLACK_PLAYER = -1;

    private final int currentPlayer;
    private final int otherPlayer;

    ...
}

I was wondering if I would get any performance hit would I choose to replace

private final int WHITE_PLAYER = +1;
private final int BLACK_PLAYER = -1;

to an enum defined as

public enum Players {
    WhitePlayer,
    BlackPlayer
}

I had the idea that enums were just syntactic sugar over integer constants, and taking a glaze look over the bytecode generated for a test enum, as well as the code calling it, seems to indicate that using them is indeed the same as making a static method call but for some enum infrastructure that is set up when it's first run.

Is my assumption that it is indeed the same to use enums as static constants correct or am I missing something here?

Upvotes: 22

Views: 17714

Answers (5)

Salix alba
Salix alba

Reputation: 7824

I'm particularly concerned with using enums in switch statements. I'll be using a program to count the number of occurrences of a finite set of symbols in a very long array.

First define some constants,

static final int NUMSYMBOLS = Integer.MAX_VALUE/100; // size of array
// Constants for symbols ZERO ... NINE
static final int ZERO_I =0, ONE_I =1, TWO_I =2, THREE_I =3, FOUR_I =4;
static final int FIVE_I =5, SIX_I =6, SEVEN_I =7, EIGHT_I =8, NINE_I =9;

and a corresponding enum.

enum Symbol {
    ZERO (0), ONE (1), TWO (2), THREE (3), FOUR (4),
    FIVE (5), SIX (6), SEVEN (7), EIGHT (8), NINE (9);
    final int code;
    Symbol(int num) {
        code = num;
    }
    public final int getCode() {
        return code;
    }
}

The enum has a field code set by a constructor. We will use this code in our testing later, which can yield some speed-up.

The set of symbols is stored in an array, and a corresponding int array.

Symbol[] symbolArray;
int[] intArray;

The symbols are counted in a method.

    void testEnum() {
        for(int i=0;i<NUMSYMBOLS;++i) {
            Symbol sym = symbolArray[i];
            switch(sym) {
            case ZERO:  ++numZero;  break;
            case ONE:   ++numOne;   break;
            case TWO:   ++numTwo;   break;
            case THREE: ++numThree; break;
            case FOUR:  ++numFour;  break;
            case FIVE:  ++numFive;  break;
            case SIX:   ++numSix;   break;
            case SEVEN: ++numSeven; break;
            case EIGHT: ++numEight; break;
            case NINE:  ++numNine;  break;
            default: break;             
            }
        }
    }

and similar method for integers.

    void testInteger() {
        for(int i=0;i<NUMSYMBOLS;++i) {
            int num = intArray[i];
            switch(num) {
            case ZERO_I:  ++numZero;  break;
            case ONE_I:   ++numOne;   break;
            case TWO_I:   ++numTwo;   break;
            case THREE_I: ++numThree; break;
            case FOUR_I:  ++numFour;  break;
            case FIVE_I:  ++numFive;  break;
            case SIX_I:   ++numSix;   break;
            case SEVEN_I: ++numSeven; break;
            case EIGHT_I: ++numEight; break;
            case NINE_I:  ++numNine;  break;
            default:
                break;              
            }
        }
    }

We can use the code from the Enum to make the switch a little more efficient.

    void testEnumCode() {
        for(int i=0;i<NUMSYMBOLS;++i) {
            Symbol sym = symbolArray[i];
            switch(sym.getCode()) {             // Uses the code here
            case ZERO_I:  ++numZero;  break;
            case ONE_I:   ++numOne;   break;
            case TWO_I:   ++numTwo;   break;
            case THREE_I: ++numThree; break;
            case FOUR_I:  ++numFour;  break;
            case FIVE_I:  ++numFive;  break;
            case SIX_I:   ++numSix;   break;
            case SEVEN_I: ++numSeven; break;
            case EIGHT_I: ++numEight; break;
            case NINE_I:  ++numNine;  break;
            default:
                break;              
            }
        }
    }

Running the three methods 10 time each. Gives the following timings.

Totals enum 2,548,251,200ns code 2,330,238,900ns int 2,043,553,600ns Percentages enum 100% code 91% int 80%

Giving a noticeable time improvement for using integers. Using the code field gives timing half-way between enums and ints.

These difference in timing can easily disappear by the surrounding code. For instance of using an ArrayList rather than an array makes the timings difference vanish completely.

There is another option in using the Enum.ordinal() method. This has perfomance similar to using a getCode(). The why and wherfore of this methods depend on are discussed at Is it good practice to use ordinal of enum?.


In my application, a reverse polish calculator, this loop and switch statement, is the heart of the program, run millions of times, and it comes up in performance analysis.

There enums are used for opcodes: PUSH, POP, etc. and each command consist of an opcode with additional arguments.

enum OpCode {
   PUSH(0), POP(1), ...;
   private final int code;
   OpCode(int n) { code=n; }
   public int getCode() { return code; }
}

class Command {
    OpCode op;
    int code;
    String var;
    Command (OpCode op,String name) {
      this.op = op;
      this.code = op.getCode();
      this.var = name;
   }
}

Building the list of commands can use the enum, without needing to know about the actual int values.

Command com = new Command(OpCode.PUSH,"x");

For non critical parts of the code we can use the enum in a switch. Say in the toString() method of the Command.

public String toString() {
    switch(op) {
    case PUSH:
       return "Push "+var;
    ....
    }
}

But critical parts can use the code.

public void evaluate(Command com) {
  switch(com.code) {
  case 0: 
     stack.push(com.var);
     break;
  ....
  }
}

for that extra bit of performance.


The byte code of the switch statements are interesting. In the int examples the swicth statment compiles to:

private void testInteger(int);
Code:
   0: iload_1
   1: tableswitch   { // 0 to 9
                 0: 56
                 1: 69
                 2: 82
                 3: 95
                 4: 108
                 5: 121
                 6: 134
                 7: 147
                 8: 160
                 9: 173
           default: 186
      }
  56: aload_0
  57: dup
  58: getfield      #151                // Field numZero:I
  61: iconst_1
  62: iadd
  63: putfield      #151                // Field numZero:I
  66: goto          186
  69: aload_0
  70: dup
  71: getfield      #153                // Field numOne:I
  74: iconst_1
  75: iadd
  76: putfield      #153                // Field numOne:I
  79: goto          186
  ....

The tableswitch command efficiently jumps forward in the code depending on the value.

The code for the switch using the code (or ordinal) is similar. Just with an extra call to the getCode() method.

private void testCode(toys.EnumTest$Symbol);
Code:
   0: aload_1
   1: invokevirtual #186                // Method toys/EnumTest$Symbol.getCode:()I
   4: tableswitch   { // 0 to 9
                 0: 60
                 1: 73
                 2: 86
                 3: 99
                 4: 112
                 5: 125
                 6: 138
                 7: 151
                 8: 164
                 9: 177
           default: 190
   ....

Using just the enum the code is more complex.

private void testEnum(toys.EnumTest$Symbol);
Code:
   0: invokestatic  #176                
                // Method  $SWITCH_TABLE$toys$EnumTest$Symbol:()[I
   3: aload_1
   4: invokevirtual #179                // Method toys/EnumTest$Symbol.ordinal:()I
   7: iaload
   8: tableswitch   { // 1 to 10
                 1: 64
                 2: 77
                 3: 90
                 4: 103
                 5: 116
                 6: 129
                 7: 142
                 8: 155
                 9: 168
                10: 181
           default: 194
      }

Here there is first a call to a new method $SWITCH_TABLE$toys$EnumTest$Symbol:() this method creates an array translating the ordinal values to an index used in the switch. Basically its equivalent to

int[] lookups = get_SWITCH_TABLE();
int pos = array[sys.ordinal()];
switch(pos) {
    ...
}

The switch table creation method, calculates the table once on its first call, and uses the same table on each subsequent call. So we see two quite trivial function calls and one extra array lookup when compared to the integer case.

Upvotes: 2

Ingo Kegel
Ingo Kegel

Reputation: 47975

In a micro-benchmark, yes, checking integer constant equality will be faster than checking enum constant equality.

However, in a real application, let alone a game, this will be totally irrelevant. The things that are happening in the AWT subsystem (or any other GUI toolkit) dwarf these micro-performance considerations by many orders of magnitude.

EDIT

Let me elaborate a little then.

An enum comparison goes like this:

aload_0
getstatic
if_acmpne

An integer comparison for a small integer goes like this:

iload_0
iconst_1
if_icmpne

Obviously, the first is more work than the second, although the difference is quite small.

Run the following test case:

class Test {

    static final int ONE = 1;
    static final int TWO = 2;

    enum TestEnum {ONE, TWO}

    public static void main(String[] args) {
        testEnum();
        testInteger();
        time("enum", new Runnable() {
            public void run() {
                testEnum();

            }
        });
        time("integer", new Runnable() {
            public void run() {
                testInteger();
            }
        });
    }

    private static void testEnum() {
        TestEnum value = TestEnum.ONE;
        for (int i = 0; i < 1000000000; i++) {
            if (value == TestEnum.TWO) {
                System.err.println("impossible");
            }
        }
    }

    private static void testInteger() {
        int value = ONE;
        for (int i = 0; i < 1000000000; i++) {
            if (value == TWO) {
                System.err.println("impossible");
            }
        }
    }

    private static void time(String name, Runnable runnable) {
        long startTime = System.currentTimeMillis();
        runnable.run();
        System.err.println(name + ": " + (System.currentTimeMillis() - startTime) + " ms");
    }
}

and you will find that the enum comparison is slower that the integer comparison, on my machine by around 1.5%.

All I was saying is that this difference will not matter in a real application ("Premature optimization is the root of all evil"). I deal with performance problems on a professional basis (see my profile) and I have never seen a hot spot that could be traced to something like this.

Upvotes: 34

user207421
user207421

Reputation: 310884

Your assumption is correct. Java makes sure there is only ever one instance of the enum so that == is as efficient as comparing ints.

Upvotes: 1

ratchet freak
ratchet freak

Reputation: 48196

JIT will optimize a lot of things making things like this irrelevant after it's been running for a while

not to mention enums are more readable and more foolproof should you make an error in your code

Upvotes: 5

unbeli
unbeli

Reputation: 30228

You should care about having nice and readable code before you care about performance. Until your profiling results (no guessing!) show that enums are the bottleneck, forget about performance and use whatever is easier to understand.

Upvotes: 17

Related Questions