junkdog / entity-system-benchmarks

microbenchmarks comparing ECS (entity component system) frameworks for java
Apache License 2.0
35 stars 8 forks source link

dustArtemis benchmarks #4

Closed dustContributor closed 10 years ago

dustContributor commented 10 years ago

I feel being left out :confused:

:stuck_out_tongue: So, how does this works exactly? For now I'm installing maven, 62Mb of additional package dependencies for a linux lib is way too big.

EDIT: dustArtemis repository https://bitbucket.org/dustContributor/dustartemis

junkdog commented 10 years ago

I assume you don't have dust-artemis in a maven repo then? If I reacall correctly, dust uses mercurial for SCM? Not sure if git can add mercurial repos as sub-modules, but we'll make it work somehow.

So, how does this works exactly? For now I'm installing maven, 62Mb of additional package dependencies for a linux lib is way too big.

Once downloaded, the libraries are in stored maven's localhost repository - it's a one-off download, mostly. The downloaded dependencies are everything from maven plugins to libs necessary for building/running the modules.

dustContributor commented 10 years ago

Yup, just plain Mercurial repo clone and you should be ready to go. It has no dependencies beyond JRE 8.

I've cloned the repo from Eclipse, imported it, converted it to a Maven project. Now I'm trying to run it as a Maven build but.... Ohh, I had to run it as Maven install, now its downloading the dependencies.

EDIT: Aand running benchmarks... They're long! EDIT2: I officially don't know what I'm doing anymore.

dustContributor commented 10 years ago

I got sidetracked and managed to run Spaceship Warriors Redux using dustArtemis (helping me discover a very silly bug I had introduced!).

I managed to run the benchmarks. I copied and pasted them from artemis-odb 0.4, only changes made is that I got rid of the static factory methods in Aspect so I call "new Aspect().all(Blah.class)" instead.

Now I'm trying to figure out why exactly in 65k Entity test, "plain" benchmark, I get such a huge spike in cost to 1.5k us/op, which also artemis-odb 0.4 suffers, but artemis-odb 0.6 doesn't.

junkdog commented 10 years ago

Hi,

I managed to run the benchmarks. I copied and pasted them from artemis-odb 0.4, only changes made is that I got rid of the static factory methods in Aspect so I call "new Aspect().all(Blah.class)" instead.

Sounds good!

Now I'm trying to figure out why exactly in 65k Entity test, "plain" benchmark, I get such a huge spike in cost to 1.5k us/op, which also artemis-odb 0.4 suffers, but artemis-odb 0.6 doesn't.

There's quite a bit of optimization that went into 0.6.x:

dustContributor commented 10 years ago

entities are processed by entity.id, ascending order, resulting in better cache coherency (at least for the benchmarks, there's no guarantee a game would benefit from it, unless one is storing component data sequentially in memory - as is the case with artems-odb's PackedComponents).

Well this result is from regular artemis-odb, not the packed components benchmark. I'm guessing rebuilding the 'actives' Bag is when things get sorted in artemis-odb? I guess I'd need to test rebuild cost vs iteration gains.

In any case, dustArtemis still functions like regular artemis in this regard, removing entities from 'actives' without regards of ordering.

entity instances have a smaller footprint (only two fields, world and id, remain) - again, hopefully leading to more cache-friendly execution.

Yeah, I've been looking at all those fields for a while trying to decide if I should just get rid of them and do everything through a World instance, either outside of Entity removing all the convenience methods or inside Entity using World accessors.

UUID:s were made optional - and lazily instantiated

Good call IMHO, I didn't tested it but I'm sure that new UUID() call was pretty expensive.

entity systems use direct array access when iterating through the list of active entities (it might actually have been introduced in 0.4.0, but I think it came later).

junkdog commented 10 years ago

Well this result is from regular artemis-odb, not the packed components benchmark. I'm guessing rebuilding the 'actives' Bag is when things get sorted in artemis-odb? I guess I'd need to test rebuild cost vs iteration gains.

All component types benefit from orderered processing in the benchmarks. Rebuilding indices isn't too expensive, but certainly more expensive than not. For tuning, it should be possible to specify how often one wants to rebuild the indices, but it hasn't been implemented yet.

Good call IMHO, I didn't tested it but I'm sure that new UUID() call was pretty expensive.

Not too bad actually - I don't remember the numbers anymore, UUID showed up in profiling, but wasn't alarmingly bad - yet, it's unnecessary overhead if one doesn't need it. And when one really needs a unique id, a simple int or long is almost certainly better from a performance POV. UUID is mostly in artemis-odb in order to stay fully API compatibile with vanilla artemis.

dustContributor commented 10 years ago

For tuning, it should be possible to specify how often one wants to rebuild the indices, but it hasn't been implemented yet.

Yeah, it feels like lots of things could be tuneable in the framework. Off the top of my head, default sizes for all collections or the way of storing component arrays, since ComponentManager's Bag of components looks kinda silly, add entity with ID 0, then add entity with ID 4000, and 4000 long array gets created for two components, I tried to tackle a bit that but I haven't had much success.

Rebuilding indices isn't too expensive, but certainly more expensive than not.

I'm actually seeing if I can introduce ordered iteration in my setup but I can't, at least not in a way that would be way more expensive than just rebuilding the 'actives' Bag.

UUID is mostly in artemis-odb in order to stay fully API compatibile with vanilla artemis.

Ahh, the joys of maintaining API stability :smile:

junkdog commented 10 years ago

add entity with ID 0, then add entity with ID 4000, and 4000 long array gets created for two components

There's no easy way around it if you want O(1) retrieval, if you compress the component bag you're likely to be stuck with O(log(n)). Besides, you're only wasting ~32kb of memory.

Edit: You could ofc use a map which would give you O(1) to O(log(n)), but the extra layer of indirection would yield worse performance.

dustContributor commented 10 years ago

Besides, you're only wasting ~32kb of memory.

Per component type array. Worst case of course, still kinda annoying.

Although I do know that replacing them with hash maps isn't the best idea either, you might get more packed arrays and lower memory footprint with a specialized collection from fastutil or HPPC, but a hash lookup per component access adds up, as Ashley has shown.