Skip to content

Hotspot optimization technical details

Adrian Papari edited this page May 31, 2016 · 2 revisions

How does it work?

While the JVM is fast at invoking methods, it is somewhat more expensive for the JVM to call megamorphic methods like EntityProcessingSystem#process(Entity). This is compounded by the fact that it's invoked once per entity per system. By performing an extra compile-time/post-compile step you can decrease Artemis overhead by 25-35%. Full benchmarks can be found at junkdog/entity-system-benchmarks.

Normally, when invoking World#process, an EntityProcessingSystem is invoked as follows:

  1. EntitySystem#process: method is declared final so only one implementation - cheap for the JVM to call
  2. EntitySystem#process makes a call to abstract method #processEntities: since this one has several implementations, method invocation is much slower (10-20x is often cited, but regardless, it's much slower) - though it doesn't really matter since it's only called once per system and tick.
  3. EntityProcessingSystem#processEntities - the most common parent for concrete entity systems - declares protected abstract void process(Entity e);; as EntityProcessingSystem#process(Entity) is likely to be one of the most frequently executed methods AND has multiple implementations (one for each concrete entity processing system), the higher method invocation cost builds up
  4. In summary:
  • EntitySystem#process: monomorphic
  • EntityProcessingSystem#processEntities: megamorphic
  • EntityProcessingSystem#process(Entity): megamorphic and invoked once per entity per system, every tick.

Ideally, we'd like to get rid of the expensive call to EntityProcessingSystem#process(Entity),

Practical

  • Scans all classes, looking for those directly extending EntityProcessingSystem
  • Replaces super class: s/EntityProcessingSystem/EntitySystem
  • Injects processEntities, copied from EntityProcessingSystem. Method is already megamorphic, so it doesn't affect performance noticeably if we add a few more.
  • Rewrite #process(Entity) as a private method (override behavior with @PreserveProcessVisiblity)
    • Since #process(Entity) is only declared abstract in EntityProcessingSystem, we are no longer invoking a polymorphic method; ie, the method is now monomorphic.
Clone this wiki locally