How big is your game object? (me 20KB)

A place for Ogre users to discuss non-Ogre subjects with friends from the community.
Post Reply
hyyou
Gremlin
Posts: 164
Joined: Wed Feb 03, 2016 2:24 am
x 5
Contact:

How big is your game object? (me 20KB)

Post by hyyou » Wed Aug 02, 2017 10:59 am

I am creating a totally-in-house Entity-Component-System game framework.

I found that a simple entity consumes about 3-20 kilobytes. (can be optimized if I am not lazy)
  • - an empty entity : 1.7 KB. .......................... for fast query and sparse set
    - an Ogre graphic entity : 5 KB. ..................... encapsulate 1 item
    - (guess) a Bullet entity : 5 KB. ..................... encapsulate 1 rigidBody
    - (guess) an enemy-ship : would use 20 KB. ...... a quite-complex entity : POD + physic + graphic
I am targeting PC specification= 1GB (magic number), Windows 7 32 bits.
In the worst case, I assume that Ogre will use 20% and Bullet use 20%, random utility=20%.
Thus, my ECS will get around 400MB.
I will waste from memory hole / allocation overhead at most 50% -> the rest = 200 MB.
I can create around 200MB/20KB = 10,000 entity - more than enough.

I think it is a little too big, though.
How large is game object in your games?
0 x

al2950
OGRE Expert User
OGRE Expert User
Posts: 1200
Joined: Thu Dec 11, 2008 7:56 pm
Location: Bristol, UK
x 76

Re: How big is your game object? (me 20KB)

Post by al2950 » Wed Aug 02, 2017 3:13 pm

Interesting question

Memory is not a constraint for me, but I have just checked and I was however a little shocked how lazy I have been in places, so I may refactor a few things but currently

An entity is ~ 50Bytes this is an 'empty entity' with no components associated with it, but it does look after scene hierarchies.
TransformComponent - 28 bytes (Equivalent to an Ogre SceneNode)
LightComponent - 24 Bytes
PhysicsComponent - 50 Bytes (very approx, still in design!)
GraphicMeshComponent - 8 Bytes (but will probably grow)

This gives you an idea, please note this does not include the memory required by Ogre or physics engine, its purely my Entity-Component system.

Also note that my component system is completely separate from Ogre or any other library, the only thing I use is Ogre's Maths library as I am lazy and its very good! My first design tried to wrap around Ogre like what you have done (or at least sounds like that), but sadly it quickly grew and ogre code started infecting my code base uncontrollably :(. I am much happier now I have got a clear boundary between my engine and the libraries I use, the only downside is you have to think hard about how you synchronise your ECS to the libraries you use, hence my other post, and as a result as I am effectively duplicating a lot of the 'state' data.

....

Just re-read your post and I cant be reading it right, one Ogre Graphic entity, assumed to be an entity with a Graphic Component which encapsulates an Ogre Item pointer is 5KB. Could you please give a bit more details of what is in that object!?

***EDIT***
Size of objects above are on 64bit system, ie 8 byte pointers
0 x

hyyou
Gremlin
Posts: 164
Joined: Wed Feb 03, 2016 2:24 am
x 5
Contact:

Re: How big is your game object? (me 20KB)

Post by hyyou » Thu Aug 03, 2017 5:53 am

Thank al2950. I will use your figures as my ideal target. You must be very good to use that low. :D

I waste a lot of memory from :-
  • UniqueVoidPointer = 24 bytes
    (pointer to its allocator, content address, my allocated address, corruption guard, stack index)
  • [#] +50% from use lot of very-low-useful redundants map int<-->entity / int<-->component ID-generator.
Detailed profile (cost per 1 ogre-graphic entity):-

Entity = 228 ................................... (mainly contain pointer to 50 components)
Entity Id generator = 16+44 = 60 ............... ([#] 8 or 12 should be enough)
graphic component : Total = 208 bytes
  • base component = 36 ..................... (a lot of debug-id / flag : useful for debugging)
  • blendmode, depthWrite, depthRead, alphaRead, cull, hologramMode, hologramMagnitude
  • lightEnable, 7 colors just in case (7*4*4 bytes), textureManipulate (7*4 bytes)
  • smokeFlag, billboardType, billboardUp, billboardAngle
transform component = 36+mat44 = 116 bytes
component standard overhead = 144 (base + ogre_item + transform)
  • per component = 48 ([#] 8-12 may be enough), used to generate id and fast query
smart pointer = 200
  • make component of different entity point each other O(1) without hashing
  • work even for 1:N
  • cost = 100 bytes per related database ([#] might be 20)
Database entity<-->entity = 276 ........... ([#] should be 0)

Summation
In theorically, it takes around 1.2 KB per my own graphic entity.
To decouple my graphic logic from Ogre (avoid "infection"), I create another Ogre-specific entity which cost around 0.8 KB.
It enables me to create a multi-pass object or change mesh easily, or even do multithreading like the last tutorial.

Thus, 1 graphic entity that is ready to render = 2KB.

I notice that I also waste from :-
  • +70% on top of it from overhead of internally-use allocator inside each system.
    +30% on top of it to make every allocator extendable at runtime.
This may be because I divide memory too hierarchy like this :-
  • global memory = global heap + global stack
  • global heap = system 1's heap + system 2's heap + ... + 20's (system_main_entity is one of them)
  • system_main_entity 's heap = pool for entity + for component 1 + for component2 + .... + 50's
  • (Now, I think I should flatten it.)
Finally (and shamefully), it becomes 5KB.

Thank for encouraging me to do the full profile.
It makes me see a better picture whether/where to optimize.
If you have any suggestion, feel free to teach me :mrgreen:.
0 x

al2950
OGRE Expert User
OGRE Expert User
Posts: 1200
Joined: Thu Dec 11, 2008 7:56 pm
Location: Bristol, UK
x 76

Re: How big is your game object? (me 20KB)

Post by al2950 » Thu Aug 03, 2017 11:37 am

Please keep in mind, my components have relatively little functionality compared to yours, but still I think you could benefit by trimming yours! Saving ram is one thing, but it looks like your design will not play too nicely with modern CPU Cache's, and so you will potentially loose performance as well.

A few comments (Disclaimer - This is purely my current thoughts, and may be completely wrong, and I may even change my opinions next week!)

- I spent about 2 years designing in my head how my Entity-Component system might work, and I went through a lot of iterations. All I can say from that research is there is no right answer that suits all use cases! However I got a lot of inspiration from EntityX as well as UE4 and unity. I could not use entityX due to the way Component IDs are generated and a few other specific issues to my system, otherwise I probably would have used it.

- Your Graphics component is big, I would be tempted to separate out some of those into there own components. In my system I have components that take up 0 Bytes. This is because I uses EntityX's concept of having a bitmask on each entity which describes what components it has associated with it. So in yours you could maybe have a Smoke Component which is separate to GraphicsComponent, etc
hyyou wrote: Entity = 228 ................................... (mainly contain pointer to 50 components)
- By the sounds of this you have a pointer to every possible component in every entity? This is probably going to annoy you at somepoint, but it depends how expandable you want your engine. It is certainly wasteful. Again I would suggest using enityX's concept of have a bitmask on each entity, if you wanted to allow up to 50 components per entity then you would use a 64bit bitmask, and that would be only 8 bytes :).

- You seem to also be heading down an inheritance design for your components, this is a slippery route and I would advise against it!

- I dont full understand the smart pointer part. ECS have 2 problems (in my opinion) that can be difficult to solve. 1) how to deal with interactions between 2 different components, 2) how to iterate over and update all components of a specific type. Both problems are not necessarily that difficult to solve, but getting them to perform well, with a clean code base and work for a wide range of use cases, can be tricky! Again EntityX shows some sensible ways of doing this.
0 x

hyyou
Gremlin
Posts: 164
Joined: Wed Feb 03, 2016 2:24 am
x 5
Contact:

Re: How big is your game object? (me 20KB)

Post by hyyou » Thu Aug 03, 2017 2:07 pm

I am trying to dig into EntityX source code (thank!), for Entity->component part,
to study how to avoid "50 components" syndrome.

In EntityX, I feel that every entity has to allocate memory for every type of components, like this :-
Image
The whole 4 blocks must be allocated. The "unused" portion is wasted.

Evidence
(https://github.com/alecthomas/entityx/b ... x/Entity.h - line 640)

Code: Select all

template <typename C, typename ... Args>
  ComponentHandle<C> assign(Entity::Id id, Args && ... args) {
    assert_valid(id);
    const BaseComponent::Family family = component_family<C>();
    assert(!entity_component_mask_[id.index()].test(family));

    // Placement new into the component pool.
    Pool<C> *pool = accomodate_component<C>();
::new(pool->get(id.index())) C(std::forward<Args>(args) ...);        //"id.index()"  Oh no!
The "id" is Entity's ID, not component ID.
Therefore, it can be far larger than amount of really used components. (thus potential "unused" slot)

Then I looked into what pool do (pink and blue arrow in image):-
(https://github.com/alecthomas/entityx/b ... elp/Pool.h line 55)

Code: Select all

inline void *get(std::size_t n) {
    assert(n < size_);
    return blocks_[n / chunk_size_] + (n % chunk_size_) * element_size_;       //  no!
  
}
.... protected:
  std::vector<char *> blocks_;
^ I believe 1 pool = 1 specific type of component ;

I think it is probably worse than mine.
Mine waste for pointer(4-8 bytes), Entityx waste for data of component(usually > 8 bytes). :shock:

I may miss something important e.g.
  • Entityx can avoid allocate the whole block if there are 0 components in a block.
  • It is still a lot of waste, except it uses relocation.
0 x

al2950
OGRE Expert User
OGRE Expert User
Posts: 1200
Joined: Thu Dec 11, 2008 7:56 pm
Location: Bristol, UK
x 76

Re: How big is your game object? (me 20KB)

Post by al2950 » Thu Aug 03, 2017 2:44 pm

You are indeed correct, and that is one of the reasons why I did not go with entityX. There is some interesting blogs around about managing memory in ECS type systems, and entityX is implementation is a fairly sensible and easy to implement system. But it has 2 issues, memory waste, and cache misses when iterating over components of a particular type.

So I am actually in the process of improving this in my system. In my system you can have register a custom memory manager for a component type, and then on ComponentMemoryManager::Get(EnitityIndex) you can have a lookup table that links the EnityIndex to an index in your memory pool. This will require extra processing, but should be fairly quick as lookup tables should be small and happily fit in cache. At the moment I use inheritance and polymorphism to implement this functionality which incurs a vtable lookup at a very performance sensitive area, so I am working on a 'nasty' meta template programming example to remove the vtable lookup overhead.

There are quite a few blogs on this subject, although can only find one at the moment
http://t-machine.org/index.php/2014/03/ ... us-memory/

As I said before there is no one correct answer, every solution has trade offs! If memory is more of an issue for you then you may be happy sacrificing some performance with Virtual functions.

**EDIT**
Here are some more blogs
http://bitsquid.blogspot.co.uk/2014/09/ ... ystem.html
http://www.randygaul.net/2013/05/20/com ... ne-design/
1 x

hyyou
Gremlin
Posts: 164
Joined: Wed Feb 03, 2016 2:24 am
x 5
Contact:

Re: How big is your game object? (me 20KB)

Post by hyyou » Sat Aug 05, 2017 3:27 am

Wow, they are ones of the top quality ECS articles, indeed.

Thank for encouraging me to read them more carefully again :D .
To keep me sane, I think I should state some disadvantages of the approaches of 3 links.

1. http://t-machine.org/index.php/2014/03/ ... us-memory/ :
  • In the final solution, I believe it is (moderately) slower from cache miss if I want something like this :-

    Code: Select all

    Entity pirateShip = ....
    ComponentHandle<Position> pos1=pirateShip;    //access some content
    Array<Entity> pirateTurrets = getAllTurrets(pirateShip);   //using my super fast pointers (aka map)
    for(auto pirateTurret: pirateTurrets ){
        ComponentHandle<Position> pos2 = pirateTurret;
        pos2->pos=pos1->pos + Vec3(1,0,0);       
        //^--- memory must be jump among the whole big array of EVERY entity
    };
    
    In my old ECS, it is a bottle neck. I am not sure whether it will happen again with this design.
    Guessed workarounds 1) iterate from child (turret) instead of parent (ship) ; 2) relocation ;
  • User should list all type components for a certain "type" of entity in advance (minor annoying disadvantage)
  • hard to add/remove component from an entity (can be workarounded probably with performance penalty)
  • Summation of size for every used component of every entity must be < a user-defined number, e.g. 5KB,
    and the 5KB memory will be allocated for every entity. (minor - bad for some rare cases)
  • The final design focuses on Component->Entity which is relatively easy and claim that it "could fit in the cache at once".
    However, it simply does not describe how to query a certain type of component from an entity - which is relatively harder.
  • (major) Mildly-to-moderate lost data locality if I just want data of 1 type of component. (see an image from http://gameprogrammingpatterns.com/data-locality.html and https://gamedev.stackexchange.com/quest ... ear-memory)
2. http://bitsquid.blogspot.co.uk/2014/09/ ... ystem.html
It uses hash map for entity-->component. It is quite slow. (not sure much, I faintly remember some profiling.)

3. http://www.randygaul.net/2013/05/20/com ... ne-design/
  • I like this.
  • It suffers the "50 components" syndrome.
  • Limit type of component to e.g. 50 (hard-coded).
  • The waste memory for 50 components is unavoidable.
  • The hard-coded aspect can be solved by using custom memory allocator (or just use below solution).
  • The cache miss aspect can be partially reduced by using 1 sparse map for each component type rather than store all of them in entity
    (thank for the idea :D).
  • (major) Inherently, query Component<->Component of the same entity is significantly slower than the first approach
    e.g. iterate n cache for n components simutaneously in random order.
Edit:
... So in yours you could maybe have a Smoke Component which is separate to GraphicsComponent, etc ....
- You seem to also be heading down an inheritance design for your components, this is a slippery route and I would advise against it!
I am afraid so. It is quite (too?) large. May you clarify/criticize a bit more, please?
More information: every field of my graphic component is used in a custom HLMS implementation :-
  • pass to byte buffer e.g. color, hologramMagnitude
  • control @property e.g. smokeFlag, hologramFlag
All of them are supposed to be used only for graphic.
Thus, I still think smokeFlag should be a part of the component.
I have a bad habit - tend to go back to code in the old OO style, though.
0 x

Post Reply