Pool
Object Pool
Intent
Improve performance and memory use by reusing objects from a fixed pool instead of allocating and freeing them individually.
Motivation
The Object pool pattern allows us to quickly allocate and deallocate objects.
The Curse of fragmentation
Imagine you have a 16-byte chunk of memory:
But there’s one two-byte chunk in the middle, so all you have is an 8-byte chunk and a 6-byte chunk.
You get a request for a 12-byte chunk, which you could allocate size wise, but you can’t allocate because you don’t have a free 12-byte chunk.
Keep in Mind
Normally you use new
and delete
to handle memory management for you.
Here are some things to keep in mind:
- The pool may waste memory on unneeded objects
- Only a fixed number of objects can be active at any one time: What happens when we need to allocate a new object?
- Prevent it outright: tune pool sizes
- Don’t create the objects
- Forcibly kill an existing object
Memory size of each object is fixed
- Arrays only hold items of the same size. Instead of allocating a lot of items with small, medium, large sizes, you might want to create arrays for small sized objects, medium sized objects, and large sized objects.
Unused objects will remain in memory
- You can’t use it with garbage collection
Sample Code
Start by implementing a simple particle class:
class Particle {
public:
Particle()
: framesLeft_(0)
{}
void init(double x, double y,
double xVel, double yVel, int lifetime) {
x_ = x; y_ = y;
xVel_ = xVel; yVel_ = yVel;
framesLeft_ = lifetime;
}
void animate() {
if (!inUse()) return;
framesLeft_--;
x_ += xVel_;
y_ += yVel_;
}
bool inUse() const { return framesLeft_ > 0; }
private:
int framesLeft_;
double x_, y_;
double xVel_, yVel_;
};
And then we create a pool that owns all these particles:
class ParticlePool {
public:
void create(double x, double y,
double xVel, double yVel, int lifetime);
void animate() {
for (int i = 0; i < POOL_SIZE; i++) {
particles_[i].animate();
}
}
private:
static const int POOL_SIZE = 100;
Particle particles_[POOL_SIZE];
};
A new particle can be created as well:
void ParticlePool::create(double x, double y,
double xVel, double yVel,
int lifetime) {
// Find an available particle.
for (int i = 0; i < POOL_SIZE; i++) {
if (!particles_[i].inUse()) {
particles_[i].init(x, y, xVel, yVel, lifetime);
return;
}
}
}
You’ll notice that every time we want to allocate more memory, this costs O(n) time to traverse to the end of the pool. We can get away with using a union and a free list:
class Particle {
public:
// ...
Particle* getNext() const { return state_.next; }
void setNext(Particle* next) { state_.next = next; }
private:
int framesLeft_;
union {
// State when it's in use.
struct {
double x, y;
double xVel, yVel;
} live;
// State when it's available.
Particle* next;
} state_;
};
The particle pool keeps a pointer to the first available particle:
class ParticlePool {
// ...
private:
Particle* firstAvailable_;
};
Then we provide a constructor:
ParticlePool::ParticlePool() {
// The first one is available.
firstAvailable_ = &particles_[0];
// Each particle points to the next.
for (int i = 0; i < POOL_SIZE - 1; i++) {
particles_[i].setNext(&particles_[i + 1]);
}
// The last one terminates the list.
particles_[POOL_SIZE - 1].setNext(NULL);
}
And a way to create a particle:
void ParticlePool::create(double x, double y,
double xVel, double yVel,
int lifetime) {
// Make sure the pool isn't full.
assert(firstAvailable_ != NULL);
// Remove it from the available list.
Particle* newParticle = firstAvailable_;
firstAvailable_ = newParticle->getNext();
newParticle->init(x, y, xVel, yVel, lifetime);
}
And when a particle dies, we add it back to the free list:
bool Particle::animate() {
if (!inUse()) return false;
framesLeft_--;
x_ += xVel_;
y_ += yVel_;
return framesLeft_ == 0;
}
Threading it back properly:
void ParticlePool::animate() {
for (int i = 0; i < POOL_SIZE; i++) {
if (particles_[i].animate()) {
// Add this particle to the front of the list.
particles_[i].setNext(firstAvailable_);
firstAvailable_ = &particles_[i];
}
}
}
Prev: dirty-flag Next: spatial-partition