22

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?

devoured elysium
  • 101,373
  • 131
  • 340
  • 557
  • hundred of thousands of times per what time unit? – forty-two Nov 06 '11 at 17:58
  • 2
    good question. i'll run the games 24/7 for (I estimate) some days, so the faster they end, the sooner I get to analyse their results. i beg people to not turn this into the typical war against performance tuning thread.. – devoured elysium Nov 06 '11 at 17:59
  • 7
    If it thought it really mettered, I would measure it. – forty-two Nov 06 '11 at 18:10
  • 1
    Without you telling us WHAT you're even doing with the enums we can't answer that question even if we wanted. – Voo Nov 06 '11 at 22:23
  • @devouredelysium I don't think there is a war against performance tuning. Most people just advise against premature optimization. See my (heavily downvoted) answer on how much slower the enum comparison is (~ 1.5%). – Ingo Kegel Nov 07 '11 at 08:41

5 Answers5

32

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.

Ingo Kegel
  • 46,523
  • 10
  • 71
  • 102
  • 2
    "However, in an 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." so you are assuming there is indeed, a GUI for this game? totally false! – devoured elysium Nov 06 '11 at 18:01
  • 1
    This benchmark could be making enums seem better than they actually are because of locality. I'd like to see how fast it performs if the enumeration access causes a TLB miss. I think that is a more real world test. I'm also surprised the optimizer didn't zilch out those loops. – johnnycrash May 19 '15 at 18:08
17

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.

unbeli
  • 29,501
  • 5
  • 55
  • 57
5

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

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
1

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.

Salix alba
  • 7,536
  • 2
  • 32
  • 38
1

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

user207421
  • 305,947
  • 44
  • 307
  • 483