I'm designing a memory managing container, with performance and ease of use in mind, especially for game development projects. Here's it in it's current state.
I'll extract the most important parts from the source.
// Uptr is a typedef for std::unique_ptr
class MemoryManageable {
bool alive{true};
public: bool isAlive() const { return alive; }
};
template<typename T> struct Deleter {
bool operator()(const Uptr<T>& mItem) const { return !mItem->isAlive(); }
};
template<typename T> class MemoryManager {
// T is the type of items being stored and must inherit MemoryManageable
std::vector<Uptr<T>> items;
std::vector<T*> toAdd; // will be added to items on the next refresh() call
Deleter<T> deleter;
void refresh() {
items.erase(std::remove_if(std::begin(items), std::end(items), deleter), std::end(items));
for(const auto& i : toAdd) items.push_back(Uptr<T>(i)); toAdd.clear();
}
void clear() { items.clear(); toAdd.clear(); }
// Del sets alive to false, so that the item will be deleted and deallocated on the next refresh() call
void del(T& mItem) { mItem.alive = false; }
template<typename TType, typename... TArgs> TType& create(TArgs&&... mArgs) { /* creates a new TType* (derived from T) and puts it in toAdd */ }
template<typename... TArgs> T& create(TArgs&&... mArgs) { return create<T, TArgs...>(std::forward<TArgs>(mArgs)...); }
}
You can see a real usage here.
The desired usage is something like this:
struct Entity : public MemoryManageable {
Manager& manager;
void destroy() { manager.del(*this); }
...
}
struct Mnnager {
MemoryManager<Entity> mm;
void del(Entity& mEntity) { mm.del(mEntity); }
...
}
Manager::update() {
mm.refresh(); // entities with 'alive == false' are deallocated, and entities in 'mm.toAdd' are added to 'mm.items'
for(auto& entity : mm) entity->update(); // entities 'die' here, setting their 'alive' to false
}
This kind of delayed insertion design with refresh()
has some great advantages:
populate()
is calledHowever, I would love if there was no need of inheriting MemoryManageable
, and if there was a more elegant way to delete entities.
MemoryManager
handle the alive
bool internally, without having to inherit MemoryManageable
, and, most importantly, without any performance overhead?MemoryManager
?Ideally, items handled by the MemoryManager
should know nothing about it.
Example usage: in gamedev, it's common that an entity gets destroyed during its update. Consider a "Enemy" entity with a int life member: if(life <= 0) this->destroy();
- that would happen easily during an update loop, and if the entity, on destruction, is immediately removed from the Manager, it causes trouble with looping and other entities that point to the dead entity.
Exceed a Container’s memory limit. A Container can exceed its memory request if the Node has memory available. But a Container is not allowed to use more than its memory limit. If a Container allocates more memory than its limit, the Container becomes a candidate for termination.
Here is the configuration file for a Pod that has one Container with a memory request of 50 MiB and a memory limit of 100 MiB: In the args section of the configuration file, you can see that the Container will attempt to allocate 250 MiB of memory, which is well above the 100 MiB limit.
In general, when an application uses more memory than the container memory, the application terminates. The following sample application pushes records at an interval of 10 milliseconds. This fast interval makes the heap grow without bounds, simulating a memory leak.
You can currently specify two types of requirement, requests and limits, for two types of container resource: memory and CPU. This post aims to explain what these two types of requirement mean and the meaning of memory within the Docker container runtime.
First thing to say: I'm not very fond of C++11, so there could be some syntax error in the code I wrote, but it's the logic that matters.
If I understood your question, you just want to be able to asynchronously add and delete items from a container, without those knowing of their state. In this scenario you can use a std::map< std::unique_ptr< Elem >, bool >
to handle the state of items: true
= alive, false
= dead.
fields:
std::vector< T * > m_toAdd
, a vector with not yet added items;std::map< std::unique_ptr< T >, bool > m_items
, a map with every item managed and a bool
flagmethods:
add()
, which adds a new item in the m_toAdd
vector;del()
, which marks an item to be removed in m_items
using its flag;refresh()
, which removes dead items and commits m_toAdd
items ad alive in m_items
, then clears the vectorfields:
MemoryManager & m_manager
, a reference to its memory manager;methods:
Elem()
, ctor, which calls m_manager::add()
;del()
, which calls m_manager::del()
.When an Elem
is created, it automatically add itself to its memory manager, which adds it to its m_toAdd
vector, then when everything is refreshed, those Elem
s in this vector are passed in the std::map
paired with a true
boolean (initially marked as alive).
When an Elem
is to be deleted, it calls a del()
method of its manager which simply marks it as dead in its std::map
, then when everything is refreshed, every Elem
in the manager flagged as dead is removed, and the m_toAdd
vector is cleared.
(Also I reccomend you using std::enable_shared_from_this
in order to better handle the ptr, but you'll have to use std::shared_ptr
, not std::unique_ptr
, but that could not be so bad.)
My suggestion is something like:
template< class T >
class MemoryManager
{
typedef std::unique_ptr< T > unique_t;
typedef std::map< unique_t, bool > map_t;
typedef std::pair< unique_t, bool > pair_t;
typedef std::vector< T * > vector_t;
public:
void add(T * item)
{
m_toAdd.push_back(item);
}
void remove(T & item)
{
typename map_t::iterator it = m_items.find(item);
if (it != m_items.end() )(* it).second = false;
}
void refresh()
{
// clear dead
typename map_t::iterator it = m_items.begin();
while ((it = std::find_if(it, m_items.end(), isDead)) != m_items.end())
{
m_items.erase(it++);
}
// add new
for(T & item : m_toAdd)
{
m_items.insert(std::make_pair(unique_t(item), true));
}
m_toAdd.clear();
}
void clear()
{
m_items.clear();
m_toAdd.clear();
}
protected:
bool isDead(pair_t itemPair)
{
// true = alive, false = dead
return itemPair.second;
}
private:
map_t m_items;
vector_t m_toAdd;
};
class Entity
{
public:
Entity(MemoryManager< Entity > & manager)
: m_manager(manager)
{
m_manager.add(this);
}
void die()
{
m_manager.remove(this);
}
private:
MemoryManager< Entity > & m_manager;
};
NOTE: the code is not tested and certainly broken, the important thing is the logic!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With