EnumMap basically works just as Empire Phoenix implementation.
There are two places that would benefit from using EnumMap the most: DefaultEntityData.handlers and DefaultEntity.components.
I’m planing to do implementation that uses EnumMap and implements this interfaces as I would like to reuse most of JME code. I will post it on forum, so maybe it can be the third implementation if pspeed will see some value in it.
I wrote some simple benchmarking classes that compares enum maps and current base implementations:
[java]
import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class EnumMapTestForDefaultDataEntity{
private static final int WARM_UP_LOOPS = 3000000;
private static final int TEST_LOOPS = 200000;
private static class Timings {
private final double handlersAsEnumMap, handlersAsCHashMap;
private Timings(final double enumGet, final double tableSearch) {
handlersAsEnumMap = enumGet;
handlersAsCHashMap = tableSearch;
}
}
public static void main(final String... args) throws Exception {
System.out.printf("Warming up JIT by passing %d loops\n", WARM_UP_LOOPS);
loop(WARM_UP_LOOPS);
System.out.printf("Starting test for %d loops...\n", TEST_LOOPS);
final Timings timings = loop(TEST_LOOPS);
System.out.printf("Avg timings in nanos:\nhandlersAsEnumMap:\t%f\nhandlersAsCHashMap:\t%f\n", timings.handlersAsEnumMap,
timings.handlersAsCHashMap);
}
static class ComponentHandler {
}
static class Component {
}
static class ComponentA extends Component {
}
static class ComponentB extends Component {
}
static class ComponentC extends Component {
}
static class EnitityDataWithConcurrentHashMap {
Map<Class, ComponentHandler> handlers = new ConcurrentHashMap<Class, ComponentHandler>();
ComponentHandler getHandler(final Class type) {
ComponentHandler result = handlers.get(type);
if (result == null) {
// A little double checked locking to make sure we
// don't create a handler twice
synchronized (this) {
result = handlers.get(type);
if (result == null) {
result = lookupDefaultHandler(type);
handlers.put(type, result);
}
}
}
return result;
}
private ComponentHandler lookupDefaultHandler(final Class type) {
return new ComponentHandler();
}
}
enum ComponentEnum {
COMPONENT_A, COMPONENT_B, COMPONENT_C;
}
static class EnitityDataWithEnumMap {
EnumMap<ComponentEnum, ComponentHandler> handlers = new EnumMap<ComponentEnum, ComponentHandler>(ComponentEnum.class);
{
handlers.put(ComponentEnum.COMPONENT_A, new ComponentHandler());
handlers.put(ComponentEnum.COMPONENT_B, new ComponentHandler());
handlers.put(ComponentEnum.COMPONENT_C, new ComponentHandler());
}
ComponentHandler getHandler(final ComponentEnum type) {
return handlers.get(type);
}
}
private static Timings loop(final int count) throws Exception {
double tableSearch = 0;
double enumGet = 0;
long start = 0;
final Component[] componentsByClass = new Component[] { new ComponentA(), new ComponentB(), new ComponentC() };
for (int i = 0; i < count; ++i) {
final EnitityDataWithConcurrentHashMap ed = new EnitityDataWithConcurrentHashMap();
final Class<Component> toFind = (Class<Component>) componentsByClass[i % 3].getClass();
start = System.nanoTime();
ed.getHandler(toFind);
tableSearch += System.nanoTime() - start;
}
for (int i = 0; i < count; ++i) {
final EnitityDataWithEnumMap ed = new EnitityDataWithEnumMap();
final ComponentEnum toFind = ComponentEnum.values()[i % 3];
start = System.nanoTime();
ed.getHandler(toFind);
enumGet += System.nanoTime() - start;
}
return new Timings(enumGet / count, tableSearch / count);
}
}
[/java]
[java]
import java.util.EnumMap;
public class EnumMapTestForDefaultEntity {
private static final int WARM_UP_LOOPS = 3000000;
private static final int TEST_LOOPS = 200000;
private static class Timings {
private final double enumGet, tableSearch;
private Timings(final double enumGet, final double tableSearch) {
this.enumGet = enumGet;
this.tableSearch = tableSearch;
}
}
public static void main(final String... args) throws Exception {
System.out.printf("Warming up JIT by passing %d loops\n", WARM_UP_LOOPS);
loop(WARM_UP_LOOPS);
System.out.printf("Starting test for %d loops...\n", TEST_LOOPS);
final Timings timings = loop(TEST_LOOPS);
System.out.printf("Avg timings in nanos:\nenumGet:\t%f\ntableSearch:\t%f\n", timings.enumGet, timings.tableSearch);
}
static class Component {
}
static class ComponentA extends Component {
}
static class ComponentB extends Component {
}
static class ComponentC extends Component {
}
static class EnitityWithTable {
Component[] components = new Component[] { new ComponentA(), new ComponentB(), new ComponentC() };
<T extends Component> T get(final Class<T> type) {
for (final Component c : components) {
if ((c != null) && (c.getClass() == type)) {
return type.cast(c);
}
}
return null;
}
}
enum ComponentEnum {
COMPONENT_A, COMPONENT_B, COMPONENT_C;
}
static class EnitityWithEnum {
EnumMap<ComponentEnum, Component> enumMap = new EnumMap<EnumMapTestForDefaultDataEntity.ComponentEnum, EnumMapTestForDefaultDataEntity.Component>(ComponentEnum.class);
{
enumMap.put(ComponentEnum.COMPONENT_A, new ComponentA());
enumMap.put(ComponentEnum.COMPONENT_B, new ComponentB());
enumMap.put(ComponentEnum.COMPONENT_C, new ComponentC());
}
Component get(final ComponentEnum type) {
return enumMap.get(type);
}
}
private static Timings loop(final int count) throws Exception {
double tableSearch = 0;
double enumGet = 0;
long start = 0;
final Component[] componentsByClass = new Component[] { new ComponentA(), new ComponentB(), new ComponentC() };
for (int i = 0; i < count; ++i) {
final EnitityWithTable enitityWithTable = new EnitityWithTable();
final Class<Component> toFind = (Class<Component>) componentsByClass[i % 3].getClass();
start = System.nanoTime();
enitityWithTable.get(toFind);
tableSearch += System.nanoTime() - start;
}
for (int i = 0; i < count; ++i) {
final EnitityWithEnum enitityWithEnum = new EnitityWithEnum();
final ComponentEnum toFind = ComponentEnum.values()[i % 3];
start = System.nanoTime();
enitityWithEnum.get(toFind);
enumGet += System.nanoTime() - start;
}
return new Timings(enumGet / count, tableSearch / count);
}
}
[/java]