I'm currently implementing ECS in "classic" sense, as defined by this wonderful post, where we have Entities, Components and Systems. And I'm having a serious (for me) performance issues. They come from the fact that System may need access to several components of the entity:
IList<int> es = _ecsManager.GetComponentEntities(ComponentType.RENDER);
int cacheRender = -1;
int cacheMovement = -1;
foreach (var entityId in es) {
var rc = _ecsManager.GetEntityComponent<RenderComponent>(entityId, ComponentType.RENDER, ref cacheRender);
var mc = _ecsManager.GetEntityComponent<MovementComponent>(entityId, ComponentType.MOVEMENT, ref cacheMovement);
rc.Sprite.Position = mc.Position;
QueueSprite(rc.Sprite);
}
the QueueSprite
is my sprite batching, it's backed by regular arrays and List
s with no dynamic in long run. And GetEntityComponent
is:
public T GetEntityComponent<T>(int entityId, int componentId, ref int idxCache) where T : PslComponent {
var cs = GetEntityComponents(entityId);
// try for cache
if (idxCache != CACHE_NULL && idxCache < cs.Count) {
var tmp = cs[idxCache];
if (tmp.TypeId == componentId) {
return (T)tmp;
}
// cache miss
idxCache = CACHE_NULL;
}
// cache failed, sequental search
for (var i = 0; i < cs.Count; i++) {
var component = cs[i];
if (component.TypeId == componentId) {
// update cache
idxCache = i;
return (T)component;
}
}
// not found
return null;
}
and GetEntityComponents
:
public IList<PslComponent> GetEntityComponents(int entityId) {
return _componentsByEntity[entityId];
}
with _componentsByEntity
being a array, GetComponentEntities
works similarly backed by regular array too:
public IList<int> GetComponentEntities(int componentId) {
return _entitiesByComponent[componentId];
}
the abovementioned foreach
loop is taking a lot of time. More than rendering.
As you can see, I'm pretty much tried to optimize a lot of things already, removing all Dictonary<>
-type lookups, replacing all by arrays and List
s. There is no dynamic allocation in long-run. The only problem, as I see it, is sequental search in entity Component loookup.
- Is there any possibility to improve component lookup speed, while remaining true to classic implementation?
- Is this major performance penalty is usually assumed then ECS is used? Are ECS "bad" then high performance is required?