Criticism needed on my template voodoo

Started by
16 comments, last by King Mir 11 years, 1 month ago
Default constructible isn't enough, you should only store POD types if you don't manage the object lifetimes. You can check for this at compile time with std::is_pod.

This would exclude the possibility of circular references, since State is not POD.

C++11 has some nice features for multithreading, but as it stands, you would need locks to protect the containers, not atomics. Nothing to stop you storing atomic types though.
Advertisement

All the constructors are called up front, and destructors are never called until the store is shut down - maybe call the destructor manually when the reference count gets to 0 for a slot?

If you do that, you have to store a char buffer or union instead of T. You can't call a destructor on most objects, because the destructor will still automatically be called when they go out of scope naturally, even if the destructor has already been called. Using placement new, you can initialize memory to an object, which can then be manually de-constructed. You can use placement new on a char buffer or a union that contains a type T member, initializing that member to T. Using a union is probably better, because you don't need to static cast when you access the buffer, or store a pointer to the object anywhere. But they syntax is ugly.



I'm also sceptical of the general purpose of this construct. I am not sure what problem you are trying to solve, and why this might be a good solution to anything.
The general purpose of this construct is that i'm trying to make an entity component system, but more in the direction of an attribute-behaviour variety.
Instead of having predetermined structs/classes that have specific members (say, a Physics component that has a vec2 for velocity, acceleration and a rect for the bounding box), i was trying to make a system where the components are basic types, such as vec2, vec3, rect, bool int, float, double or color. That way i could have a lot more control over what data i need in an entity. Problem is that standard component systems operate by new'ing the components on the heap, and calling new for basic types just seemed like a bad idea (considering an entity would have at least 20 properties, plus anything required by the game logic, which can be up to a hundred properties or more, depending on the game).
I've read in many places that calling new a lot is expensive, so i tried making a system where calling it was unneccessary. Instinctively, i thought of pulling these basic types from an object pool, and wrapping them with a class that could be saved by value in the entity class.
I presume that new is expensive because it needs to find the memory location to place the object in, so placement new would be cheaper if i already have the location? If i change the store to actually be a memory pool instead of an object pool, would that solve anything? Or is my whole idea just plain wrong to begin with?

devstropo.blogspot.com - Random stuff about my gamedev hobby

Your current implementation isn't cheaper than calling new.

Also, a bigger cost stemming from dynamic memory is not calling new, but the loss of cache locality that results in; a new'd object can be located far in memory from where the pointer is used and stored.


Do you need entities to be dynamically typed, or will you still be creating components like "Physics" at compile time? If the latter, could use tuples instead?
Yeah, i'm aware of the cache locality issue, and was trying to minimize it by using vectors in the DataStore class.

I wouldn't create components at compile time, but rather from Lua scripts. I was thinking of exposing the needed functionality to Lua, so i could make an Entity by dynamically giving it data that is needed for it to do something.
Physics, rendering, movement, collision and such would be done by giving it only what it needs to perform that action. If an Entity has velocity which never changes, it just wouldn't have an acceleration property. If another Entity needed acceleration, then i'd give it the acceleration property, and it would work as expected.

Each Entity would be registered to a behaviour that operates on a collection of Entities, but only if they have the required properties. For instance, there would be an Accelerate behaviour, that expects the entities in it's collection to have the Acceleration and Velocity properties, and would just add the Acceleration value to the Velocity value (and if any of the properties aren't of the expected type, the game would complain and either crash or skip that entity).

I was thinking about maybe skipping the State wrapper class, and try using DataStores directly, something like DataStore<T>::attachProperty(Entity& entity, string name, T& value); and have a T& DataStore<T>::getProperty(Entity& entity, string name), but this would probably suffer from the same issues.

devstropo.blogspot.com - Random stuff about my gamedev hobby

You have a vector of same type objects. That doesn't help cache locality, because there's no special tendency to access same type objects near each other.

Instead you probably want entities components in the same collection to be near each other. As I see it there are two efficent ways to do this:
1) a container of of union types. You might need a wrapper object on the union.
2) a container of pointers, all pointing to a char buffer, holding the data. There are various ways to achieve this.


Also, since the entire point of this voodoo is to optimise performance, you should build a testbed that actually verifies the performace improvement. Time the baseline, using new all over the place. Time this implementation (fixing the deque issue). And if you attempt something else, time that too.
Hm, true about vector locality...
I know i should probably test the performance first, and this is part of my problem, that i'm doing premature optimizations.

At any rate, i'm also willing to scrap part of this concept in favour for something better. I just don't know what that would be biggrin.png

The only thing i would really like to have is a way to hold individual data types in a non-template class, and have the non-template class take care of the cleanup.

I'm also juggling an idea where i ask a data store to make me a state, something like this:
State DataStore<T>::MakeState()
{
    return State(this, &GetAddressFromPool(), hashOfType);
//the GetAddressFromPool would be a dataStore<T> method
}

State(IDataStore* store, void* ptrToData, size_t hash) {}

~State() 
{
    store->Release(ptrToData);
}

T& State::as<T>()
{
    if(hash == typeof(T).hash_code())
    {
	return *static_cast<T*>(ptrToData));
    }
    throw exception;
}
Is this something in the lines of your 2) option?
I'm not really planning to go multithreaded any time soon, so i could get away with no thread safety, but the concept of having any data type in a non-template class is really appealing to me, and would like to pursue it.

devstropo.blogspot.com - Random stuff about my gamedev hobby

That looks like it's implementing a global memory pool for the state internals. That's better than your original, because objects of different types are in the same pool. Also, there is less indirection.

But I meant to suggest having a separate buffer for each component -- for each container with State objects in it.


Premature optimisation is one problem, but a bigger one is trying to write code without testing if it works. For performance optimisation, testing requires profiling. The surest way to see if a particular implementation is any good is not to ask here, but to test.

This topic is closed to new replies.

Advertisement