-
Notifications
You must be signed in to change notification settings - Fork 114
PackedWeaver Implementation Details
The above class looks something like this after it's been compiled:
public class PackedWeaverReference extends PackedComponent implements DisposedWithWorld {
private int $stride;
private static final int $_SIZE_OF = 8;
private static Map<World, Bag<PackedWeaverReference>> $store = new IdentityHashMap<World, Bag<PackedWeaverReference>>();
private ByteBuffer $data = null;
private World $world;
public PackedWeaverReference(World world) {
this.$world = world;
Bag<PackedWeaverReference> instances = $store.get(world);
if (instances != null) {
$data = instances.get(0).$data;
} else {
$data = ByteBuffer.allocateDirect(128 * $_SIZE_OF);
instances = new Bag<PackedWeaverReference>();
$store.put(world, instances);
}
instances.add(this);
}
@Override
protected void forEntity(Entity e) {
this.$stride = $_SIZE_OF * e.getId();
}
@Override
protected void reset() {
$data.putFloat($stride + 0, 0);
$data.putFloat($stride + 4, 0);
}
@Override
protected void ensureCapacity(int id) {
int requested = (1 + id) * $_SIZE_OF;
if ($data.capacity() < requested)
$grow(2 * Math.max($data.capacity(), requested));
}
private void $grow(int capacity) {
ByteBuffer newBuffer = ByteBuffer.allocateDirect(capacity);
for (int i = 0, s = $data.capacity(); s > i; i++)
newBuffer.put(i, $data.get(i));
for (PackedWeaverReference ref : $store.get($world))
ref.$data = newBuffer;
}
@Override
public void free(World world) {
$store.remove(world);
}
public float x() {
return $data.getFloat($stride + 0);
}
public float y() {
return $data.getFloat($stride + 4);
}
public void x(float value) {
$data.putFloat($stride + 0, value);
}
public void y(float value) {
$data.putFloat($stride + 4, value);
}
public void set(Vec2f v) {
$data.putFloat($stride + 0, v.x());
$data.putFloat($stride + 4, v.y);
}
}
Take the results with a grain of salt. There are no guarantees that the benchmark is correct, but hopefully. If you know your way around benchmarking, feel free to take a look at the code.
All benchmarks run World#process
- at every 100th iteration an entity is deleted and then
recreated. All benchmarks have an additional system updating a single component per entity,
except baseline - which only iterates all entities without actually reading/updating the
component.
plain, pooled and packed work with normal com.artemis.Component',
com.artemis.PooledComponent' and 'com.artemis.PackedComponent' respectively.
# VM invoker: /home/junkdog/opt/apps/jdk1.7.0_55/jre/bin/java
Benchmark
c.a.ComponentTypeBenchmark.baseline_world avgt 15 10.821 0.138 us/op
c.a.ComponentTypeBenchmark.packed_world avgt 15 35.276 0.533 us/op
c.a.ComponentTypeBenchmark.plain_world avgt 15 30.739 0.195 us/op
c.a.ComponentTypeBenchmark.pooled_world avgt 15 28.426 0.043 us/op
Reflecting on the results:
- The struct-like approach yields the worst performance; supposedly, due to the overhead from method calls vs direct field access.
-
EntitySystems don't internally impose any ordering on its entities - processing entities in ascendingFixed, improved performance throughout.entityId
order would reduce cache misses. #87 - ByteBuffer - injected by
@PackedWeaver
- performs boundary checks when invokingget
andput
methods. Weaving withsun.misc.Unsafe
should perform better. Support for choosing betweenjava.nio.ByteBuffer
andsun.misc.Unsafe
is planned for a post-0.6.0 release: #86 -
@PooledComponent
is likely to perform much worse in the real world. Be wary.
To manually run the benchmarks - from artemis' root folder:
mvn clean install
java -jar artemis-benchmark/target/microbenchmarks.jar -t 1 -f 5
To get less noisy results, consider:
- Rebooting the computer
- Stop any non-vital background processes
- Disable all network adapters.
- Overview
- Concepts
- Getting Started
- Using
- More guides
- Plugins
- Game Gallery
- Tools and Frameworks
- API reference