[IMPROVEMENT] Use of Primitive Stacks/Queues
See original GitHub issueHello,
I am working on game and was going to test on Artemis if it was something for my entity system. But being me I am a person who has to understand everything I use so I am cloning it to make a custom version of it.
On the way I found a couple things that were odd to me.
- Bag allocating 64 entries by default.
- EntityTransmuter requiring a Builder for a single entry (just make a constructor),
- Using 3 steps on things that could be done on a single step. (Mostly for the ComponentType related classes)
- And BitVector not having a fastEquals function even so it would make a lot of sense.
But i found actually something where the performance could be drastically improved. Your EntitySubscription rebuilds the List of Entities everytime it gets changed.
I was thinking how to solve that issue because this is just such a unnessseary performance loss especially if large systems have many changes. And it really needs just a large amount of entities in 1 system to cause a good performance reduction.
So my idea was: Use Queue/Stack for this system and make use of the bitsets, until a certain point. (Like if 35-40% of the size was removed anyway then just rebuild)
This is the logic I was thinking if there were only small changes made. (Would have to be adjusted based on the state the system is in)
Int2IntMap map = new Int2IntOpenHashMap();
IntArrayList list = new IntArrayList();
IntPriorityQueue queue = new IntArrayPriorityQueue();
DynamicBitSet presentEntities = new DynamicBitSet(); // BitVector
public void process(IntList insertions, IntList removals)
{
//Simulating here changes were already decided to be added & removed
for(int i = 0, s = removals.size();i<s;i++)
{
int id = removals.get(i);
if(presentEntities.getFast(id))
{
remove(id);
}
}
for(int i = 0, s = insertions.size();i<s;i++)
{
int id = insertions.get(i);
if(presentEntities.getFast(id))
{
continue;
}
insert(id);
}
clearEmptyHoles();
}
private void clearEmptyHoles()
{
//Queue size is just a getter so faster then making a !(size == 0)
while(queue.size() > 0)
{
int nextSlot = queue.dequeueInt();
if(nextSlot == list.size()) // Last slot is empty so we need to clear it from the Entities too and clear the queue
{
list.pop();
queue.clear();
return;
}
else if(nextSlot > list.size()) // Slot is bigger then the largest index of the list meaning its already of the list and auto cleared.
{
queue.clear();
return;
}
int id = list.popInt();
while(id == 0 && nextSlot <= list.size()) // Finding the next none empty object
{
id = list.popInt();
}
if(nextSlot > list.size())
{
return;
}
map.put(id, nextSlot);
list.set(nextSlot, id);
}
}
public void remove(int id)
{
presentEntities.clearFast(id);
int index = map.remove(id);
queue.enqueueInt(index);
list.set(index, 0);
}
public void insert(int id)
{
presentEntities.setFast(id);
if(queue.size() > 0)
{
int index = queue.dequeueInt();
list.set(index, id);
map.put(id, index);
return;
}
map.put(id, list.size());
list.add(id);
}
This system would allow not to “Constantly on any change” to rebuild the Iteration queue while using a tiny bit more memory. This is only a proof of concept but if having enough large systems would maybe have a performance improvement.
Oh yeah I know I used “FastUtil” and thats not used because its class count is so big but its just there to provide a point of what my idea is.
Edit: My game will be most likely pc only and I know android is a bit different in performance, but that is what I have thought of while building my own system of this implementation, maybe it will help.
Issue Analytics
- State:
- Created 4 years ago
- Comments:9 (2 by maintainers)
Top GitHub Comments
Other thing: The class name “World” is a really bad picked name for the “EntityComponentSystem”. Because some could just use it for “Entities” in their “World.class” which would mean they would have to implement a wrapper just to use your ECS Library (Also a reason why I manually copy it) My suggestion would be to either name the main class that you should use: “EntityComponentSystem” or “ECSWorld” So you have still a good indicator but do not try to conflict with other projects that actually want to use your Library.
A library like this should try not to colide with the most important class with a common class name.
Yeah fine by me.