Ecs Architecture Efficiency

Started by
17 comments, last by Shannon Barber 7 years, 7 months ago

I was wondering for all those C++ programmers out there, what kind of efficiency your getting from an ECS (Entity Component System) game architecture. I'm wondering about max cycles, how many entities and corresponding components you can create in your system before UPS and FPS drops below the standard 60.

Advertisement
You're asking for pretty meaningless data.

"max cycles" ? Max cycles for _what_ ? I can add 10,000,000,000,000 component-heavy game objects to my non-ECS component-based engine and at runtime it takes up 0 cycles if I never trigger an update on any of them.

"how many components" ? How many of _which_ components? Renderables have a very different affect on frame time than does physics, which have a different effect than does AI, which has a different effect than data components that never need processing.

What data? A physics body with a sphere collider is going to a helluva lot faster than a physics body with a complex multi-mesh collider structure.

Sean Middleditch – Game Systems Engineer – Join my team!

If you mean how many entities you can have updating at once... it really doesn't matter. Any well implemented schema will perform near enough. It's just that some are better at certain things than others.

Of all the articles I've read on "ECS", all of them implement the "pattern" differently. If you're an experienced programmer, I'm sure your entity system will be fast enough, of you're not an experienced programmer, I hope that your game has small enough scope that your entity system is still fast enough :wink:

Ok bad forum to ask such a question.... obviously you didn't understand me.

Ok bad forum to ask such a question.... obviously you didn't understand me.


Then by all means elaborate, or we never will. ;)

Ok let me tackle this from a different perspective.... (No I'm not an experience programmer in C++; just learned it a couple months ago.... still learning)

Say I have multiple vectors of custom types like so:


class Base {
private:
    int id;
public:
    Base(int ID) : id(ID) {}
}

class A : public Base {
public:
    int x;
    A(int ID) : Base(ID) {}
}

class B : public Base {
public:
    int y;
    B(int ID) : Base(ID) {}
}

class Controller {
public:
    std::vector<std::shared_ptr<A>> AList;
    std::vector<std::shared_ptr<B>> BList;
}

Then I have a class that's going to iterate over the 2, because A has info B needs to operate:


class DoSomething {
private:
    std::shared_ptr<Controller> MasterController(new MasterController());

public:
    DoSomething() {
        for(int i = 0; i < 500; i++) {
            std::shared_ptr<A> newA(new A(i));
            MasterController->AList.emplace_back(std::move(newA));
        }
        for(int i = 0; i < 500; i++) {
            std::shared_ptr<B> newB(new B(i));
            MasterController->BList.emplace_back(std::move(newB));
        }
    }

    void Running_DoSomething() {
        for(std::vector<std::shared_ptr<A>>::iterator itA = MasterController->AList.begin(); itA != MasterController->AList.end(); ++itA) {
            for(std::vector<std::shared_ptr<B>>::iterator itB = MasterController->BList.begin(); itB != MasterController->BList.end(); ++itB) {
                if((*itB)->id == (*itA)->id) {
                    ...Do Something >>>
                }
            }
        }
    }
}

Let's say this operation Running_DoSomething is called 60 times per second, iterating over 1 vector has very little effect on the FPS, but when I iterator over a 2nd it drops the FPS down to like 8/sec. Why is iterating over multiple vectors like this so slow? And how can I correct this to prevent the drop in max cycles (where max cycles is a measure of how many total potential cycles of the game loop occur each second)

My guess is that he is asking for the "throughput" of the framework.

ie, how many entities you can add/remove from the world per frame while not dipping below 60 fps. Same with attaching/removing components. The raw overhead of the framework you pay for having it track all of this stuff.

The answers you get are because its not an easy thing to measure in "proper" game engines and games. Its an easy thing to do in our tiny hobby projects that dont uses complex resource management sub systems, nor complex sub system initialization steps. Not so much on bigger games and engines.

I'm going to throw a reasonable guess out there that most of the time on fully featured games (with sound, graphics, ai, etc), entity handling overhead is quite tiny compared to all the other complex stuff the engine has to do (manage disc resources, upload to gpu, insert the new data on the various structures the AI/terrain/sound systems use, etc).

Ideally in an ECS the cost of doing all of these operations (entity insertion/removal, component insertion/removal, entity iteration, per sub system filtering of entities based on their components) should be low enough for you not having to worry about if you're mutating the structure of too many entities, or if you're creating/removing too many entities in a single frame. As the game's inherent complexity goes up, this (probably fixed) overhead becomes much more less relevant.

What is to say: The cost of adding an entity to the world is going to be relatively much bigger in a 2D space invaders game, compared against the same cost in the context of a more complex game, like Battlefield for example. Simply because in Battlefield you might be processing 10Mb worth of data for loading an NPC into the scene, so a couple of hash lookups for making that NPC appear in the world managed by the ECS is not a lot.

In short, its not an appropriate question without having it placed in the context of a specific game.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Well TheChubu kind of got what I was asking but not quite.... Let's stick with my question from the reply before his/hers.

Let's say this operation Running_DoSomething is called 60 times per second, iterating over 1 vector has very little effect on the FPS, but when I iterator over a 2nd it drops the FPS down to like 8/sec. Why is iterating over multiple vectors like this so slow?

Look up "big o complexity". One loop is O(N), two nested are O(N*N). Or in your example, one loop is 500 iterations, while two is 250000 iterations.

As to fixing this, the problem is that you're searching a quarter million possible pairs to find the 500 pairs that actually have matching IDs. This is a data organization problem. Seeing as there's only 500 pairs that you're interested in, you should only have to iterate that many times. You could store an index of A/B pairs in another vector, or keep your existing vectors sorted by ID and exploit this fact in your iteration algorithm.

This topic is closed to new replies.

Advertisement