Enums !

It allocates an EnumSet instance at the very least. People obsessed with bit-level performance should stick to C.

Okay, I Google’d EnumSet. Happy? ._.

Ehm… why? As said, maybe in bytecode, but not in HotSpot… it’s just an int.

If that’s directed at me… I’m just answering a question, explaining what happens underneath.

I see it allocates in bytecode / the JDK source, so it makes me wonder what magic happens and also, what Android does (ie, how Android fscks this up).

There is more to an EnumSet than an int, such as the Set interface. What may be the case is after inlining that the relevant membership tests become int operations. but HotSpot doesn’t do anything without it being bytecode first. JIT does a lot, but it’s not magic

No, it’s directed at people who are actually fretting about Enums having more overhead than ints.

For the sake of it, I benchmarked java.util.RegularEnumSet, and unfortunately even in 6u26 it suffers from the interface overhead.

   static enum Test
   {
      A, B, C, D, E, F, G, H, I, J, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z;
   }

   private static void benchmark(Set<Test> tests, Test value)
   {
      for (int i = 0; i < 16 * 1024 * 1024; i++)
      {
         tests.contains(value);
      }
   }


         Set<Test> enumSet = EnumSet.of(Test.A, Test.B);
         Set<Test> hashSet = new HashSet<Test>();
         Set<Test> treeSet = new TreeSet<Test>();
         for (Test test : enumSet)
         {
            hashSet.add(test);
            treeSet.add(test);
         }

         {
            long t0 = System.nanoTime();
            benchmark(enumSet, Test.A);
            long t1 = System.nanoTime();
            System.out.println("enum took: " + (t1 - t0) / 1000000 + "ms");
         }

         {
            long t0 = System.nanoTime();
            benchmark(hashSet, Test.A);
            long t1 = System.nanoTime();
            System.out.println("hash took: " + (t1 - t0) / 1000000 + "ms");
         }

         {
            long t0 = System.nanoTime();
            benchmark(treeSet, Test.A);
            long t1 = System.nanoTime();
            System.out.println("tree took: " + (t1 - t0) / 1000000 + "ms");

         }

If you change the order of execution of the different types of Set you can see massive performance differences.

Results for Server VM 6u26:


enum took:   7ms <-- first
hash took: 125ms
tree took: 133ms

hash took: 150ms
tree took: 104ms
enum took: 130ms <-- last

Just so you know. And yes, and instance is allocated.

Set<Test> enumSet = EnumSet.of(Test.A, Test.B);

That doesn’t look right… Only two values? Why not EnumSet.allOf(Test)?

How does that not look right…

The main issue I have is that if you want to use enums heavily, then it requires lots of enum declarations. That is a scenario where symbols/atoms work much better, because you are essentially declaring the enums within their usage, making the code much more terse.

You could then also add guards or pattern matching, for automatic verifying parameters. These would then be verified using type inference, or at runtime. But I don’t think Java could really take guards and symbols, or should really get them. Both would be moving away from Java’s static type system, which wouldn’t fit the philosophy of the language.

So I think Java enums work well for Java, but aren’t so great when compared to what you can do in other languages.

Ah, the whole test only uses those two enums. Carry on, dear sir. But I do wonder why you only use A and B instead of all…

TreeSet is not as robust as HashSet and EnumSet (contains throws ClassCastException and NullPointerException).

Improved caliper benchmark (just expanded http://code.google.com/p/caliper/source/browse/trunk/examples/src/main/java/examples/EnumSetContainsBenchmark.java).

Results:


      setMaker trial   ns linear runtime
      TREE_SET     0 49,7 ======================
      TREE_SET     1 38,6 =================
      TREE_SET     2 49,8 ======================
LARGE_TREE_SET     0 67,4 ==============================
LARGE_TREE_SET     1 58,3 =========================
LARGE_TREE_SET     2 67,0 =============================
      HASH_SET     0 30,8 =============
      HASH_SET     1 31,0 =============
      HASH_SET     2 30,9 =============
LARGE_HASH_SET     0 39,2 =================
LARGE_HASH_SET     1 39,2 =================
LARGE_HASH_SET     2 39,2 =================
      ENUM_SET     0 16,6 =======
      ENUM_SET     1 16,6 =======
      ENUM_SET     2 16,6 =======
LARGE_ENUM_SET     0 15,9 =======
LARGE_ENUM_SET     1 15,9 =======
LARGE_ENUM_SET     2 15,9 =======

import com.google.caliper.Param;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class EnumSetPerformanceTest extends SimpleBenchmark {

    @Param
    private SetMaker setMaker;

    public enum SetMaker {

        TREE_SET {

            @Override
            Set<?> newSet() {
                return new TreeSet<RegularSize>(EnumSet.allOf(RegularSize.class));
            }

            @Override
            Object[] testValues() {
                return new Object[]{RegularSize.E1, RegularSize.E2, RegularSize.E20,
                            RegularSize.E32, RegularSize.E31};
            }
        },
        LARGE_TREE_SET {

            @Override
            Set<?> newSet() {
                return new TreeSet<LargeSize>(EnumSet.allOf(LargeSize.class));
            }

            @Override
            Object[] testValues() {
                return new Object[]{LargeSize.E1, LargeSize.E63, LargeSize.E64,
                            LargeSize.E65, LargeSize.E140};
            }
        },       
        HASH_SET {

            @Override
            Set<?> newSet() {
                return new HashSet<RegularSize>(EnumSet.allOf(RegularSize.class));
            }

            @Override
            Object[] testValues() {
                return new Object[]{RegularSize.E1, RegularSize.E2, RegularSize.E20,
                            RegularSize.E32, RegularSize.E31};
            }
        },
        LARGE_HASH_SET {

            @Override
            Set<?> newSet() {
                return new HashSet<LargeSize>(EnumSet.allOf(LargeSize.class));
            }

            @Override
            Object[] testValues() {
                return new Object[]{LargeSize.E1, LargeSize.E63, LargeSize.E64,
                            LargeSize.E65, LargeSize.E140};
            }
        },
        ENUM_SET {

            @Override
            Set<?> newSet() {
                return EnumSet.allOf(RegularSize.class);
            }

            @Override
            Object[] testValues() {
                return new Object[]{RegularSize.E1, RegularSize.E2, RegularSize.E20,
                            RegularSize.E32, RegularSize.E31};
            }
        },
        LARGE_ENUM_SET {

            @Override
            Set<?> newSet() {
                return EnumSet.allOf(LargeSize.class);
            }

            @Override
            Object[] testValues() {
                return new Object[]{LargeSize.E1, LargeSize.E63, LargeSize.E64,
                            LargeSize.E65, LargeSize.E140};
            }
        };

        abstract Set<?> newSet();

        abstract Object[] testValues();
    }

    private enum RegularSize {

        E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15, E16, E17,
        E18, E19, E20, E21, E22, E23, E24, E25, E26, E27, E28, E29, E30, E31, E32
    }

    private enum LargeSize {

        E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, E15, E16, E17,
        E18, E19, E20, E21, E22, E23, E24, E25, E26, E27, E28, E29, E30, E31, E32,
        E33, E34, E35, E36, E37, E38, E39, E40, E41, E42, E43, E44, E45, E46, E47,
        E48, E49, E50, E51, E52, E53, E54, E55, E56, E57, E58, E59, E60, E61, E62,
        E63, E64, E65, E66, E67, E68, E69, E70, E71, E72, E73, E74, E75, E76, E77,
        E78, E79, E80, E81, E82, E83, E84, E85, E86, E87, E88, E89, E90, E91, E92,
        E93, E94, E95, E96, E97, E98, E99, E100, E101, E102, E103, E104, E105, E106,
        E107, E108, E109, E110, E111, E112, E113, E114, E115, E116, E117, E118,
        E119, E120, E121, E122, E123, E124, E125, E126, E127, E128, E129, E130,
        E131, E132, E133, E134, E135, E136, E137, E138, E139, E140,
    }
    private Set<?> set;
    private Object[] testValues;

    @Override
    protected void setUp() {
        this.set = setMaker.newSet();
        this.testValues = setMaker.testValues();
    }

    public void timeContains(int reps) {
        for (int i = 0; i < reps; i++) {
            set.contains(testValues[i % testValues.length]);
        }
    }

    public static void main(String... args) {
        Runner.main(EnumSetPerformanceTest.class, new String[]{"-Dsize=100,1000,10000,100000",
                    "--trials", "3"});
    }
}

Ooh, I love micro-benchmarks! :stuck_out_tongue:

Why assign them to Set rather than their concrete types if you want to test their comparative speed?

EnumSets are the one time I don’t use the general convention of using the interface for fields and method parameters anyway - using EnumSet seems somehow more semantically correct to me.

Don’t forget that EnumSets are mutable, so you can get away with allocating once in a lot of cases if you feel the need (I don’t, but then I’m not targeting Android! :slight_smile: )

I remember working on reverse engineering the communications of a game. Some objects in the game allowed the user to interact with them and a random numbr was generated. When we started digging into to communications we found that the number was being generated client side then was sent to the server so all of the clients would see the result. In this situation they were not using enums and an invalid number did one of two things, A) if set to 0 the animation for that item kept going. B) if set to more then 8 or less then 0 the item would crash all clients viewing that item.

All in all it was very entertaining to watch the problems produced by poor coding (no data verification nor range checking of the client)

I’m just saying, Java enums take so long to write I might as well just make a class. I just want MyEnum = enum { A, B, C, D} and then I can just say MyEnum.A whenever. It’s good to know that Java enums become ints under the hood, but for me enums are completely convenience anyway - when I can just use an int or a string I type all over my code if I want. Something that’s supposed to be convenient shouldn’t be so inconvenient.

I don’t get it? It works nearly as you typed.

enum MyEnum { A, B, C, D }
public static void main (String[] args) throws Exception {
	System.out.println(MyEnum.A);
}

Maybe I lie.

I’m a huge fan of enums and particularly extensible enums. One of the problems of extensible enums is that you can’t use them in EnumSet. EnumSet is necessary to perform set operations efficiently otherwise things get particularly horrendous especially on Android. I created a new collection ExtEnumSet that is like EnumSet, but supports extensible enums. I wrote more here:

https://plus.google.com/117904596907381647757/posts/aHiaoA8f6WZ

I’ve moved to the ExtEnumSet for storing entity state in my ES.

Another problem with EnumSet is that under the hood it keeps track of a unique array of Enums for the given supported type for each EnumSet. Ahh… This may be the case for Java 5, but I just checked in Java 6 source and it looks like this array is universally shared which wasn’t the case in Java 5 if I recall correctly. I actually finally had to move to Java 6 due to javac / compiler issues with some sort of generic method failure with Java 5.

Anyway… I’m a huge fan of extensible enums and they were the final missing key to really unlock state tracking between unrelated components in the component architecture I’m releasing soon; useful in so many ways. Also it’s quite easy to use enums extensible or not as components in my component architecture. I yap on a bit about that in the long ES topic on B^3’s blog:
http://t-machine.org/index.php/2011/08/22/entity-system-rdbms-beta-a-new-example-with-source/comment-page-1/#comment-30180

[edit] hah, that link to the ES comment is silly Adam / B^3 ruffled my feathers slightly, so I kind of go a little off in that reply… It’s the one where I described using enums as components though. :o The whole thread is a fairly spirited discussion of sorts…