Implementing objects with short life time but that have to be created frequently

Started by
12 comments, last by SiCrane 11 years, 5 months ago
In my project, I want players to be able to shoot bullets. So I made a bullet class.
Everytime a player shoots a bullet, bullet object is created with new operator. But I figured this leads to calling new and delete as much as 50 times per second(Guessing this is going to be a performance issue) since there are multiple players shooting at each other simultaneously.

So I considered using a "recycle bin" for bullet object. I would preallocate about 200 bullet objects and if a bullet is destroyed, I can mark the bullet dead and revive it later when needed. But then I have to search for available bullet and that searching can take time...

How should I implement this?
An invisible text.
Advertisement
You can save a lot of time once you realize that the order of the bullets in memory doesn't matter. When bullet is deleted, memcpy the last bullet in the list to the deleted bullet's place, that way all the active bullets will be first and you can bust add a new one to the end

You can save a lot of time once you realize that the order of the bullets in memory doesn't matter. When bullet is deleted, memcpy the last bullet in the list to the deleted bullet's place, that way all the active bullets will be first and you can bust add a new one to the end

I don't quite understand what you are saying. Can you be more clear?
An invisible text.
Lets say you have an array which holds 200 bullets and a variable which contains the number of bullets which are 'alive'.

When a bullet needs to be deleted you copy the last 'alive' bullet down to it's location and then reduce the number of bullets which are 'alive' in the world (unless, of course, the bullet count is '1', at which point you just reduce the count to zero).

So if you have 49 bullets 'alive' and the 3rd bullet 'dies' you would do something like bullets[3] = bullets[48]; --bulletCount;
He's saying you don't have to "search" for a dead bullet, you just swap the dead bullet with the live bullet at the end of the list and then pop the list. When you add a new bullet, just push_back(). Since the vector already has the memory reserved (see capacity), it won't have to reallocate, and wouldn't be too slow.

//Find the dead bullet's location.
iterator locOfDeadBullet = ...;

//Move the dead bullet to the end of the vector.
std::swap(myVector.back(), *locOfDeadBullet);

//Remove the last element in the vector (i.e. the dead bullet).
myVector.pop_back();
Oh thanks I see. I got confused because I didn't know he was talking about vector.
But why can't we use list here? Does list free the memory when I pop an element out?
An invisible text.
You could use a list, however, vector is better in this situation.
Why? Vector is optimized for iterating over the elements. List is optimized for inserting elements into the middle of the container.
Which will you be doing more? (Hint: You'll iterate over the entire list at least once a frame to draw the bullets. You'll probably only add a bullet only once every several frames)

Both, if used properly, can speedily add new elements to the end (vector does it speedily if the capacity is large enough, list always does it speedily). List is better for re-ordering, inserting, or removing. Vector, using the swap-to-end technique mentioned by everyone above, can pop and add easily enough when you don't require your elements to be in a specific order - which in this case you don't require.

You could use a list, however, vector is better in this situation.
Why? Vector is optimized for iterating over the elements. List is optimized for inserting elements into the middle of the container.
Which will you be doing more? (Hint: You'll iterate over the entire list at least once a frame to draw the bullets. You'll probably only add a bullet only once every several frames)

Both, if used properly, can speedily add new elements to the end (vector does it speedily if the capacity is large enough, list always does it speedily). List is better for re-ordering, inserting, or removing. Vector, using the swap-to-end technique mentioned by everyone above, can pop and add easily enough when you don't require your elements to be in a specific order - which in this case you don't require.

Ok vector is better in that aspect, but I was asking if I pop an element out from a list, does list free its memory unlike vector? A vector doesn't shrink even if I pop an element out, so it doesn't have to re allocate the memory.

So for a list, to add a bullet, it first uses new to allocate a space, and then constructs the bullet object.
But for a vector, it constructs the bullet object right away because memory is already allocated.

Am I right?
An invisible text.
I think you are doing this in the classical wrong order. That is, optimizing before doing profiling measurements? 50 times per second is probably not registering at all in a CPU load.

You should stay with the "simple solution" that is working, and then replace it when you have performance problems and found that this is a significant resource problem.

That said, the question can still be interesting to discuss, from a design point of view. One thing that is good to do, however, is to code the algorithms in such a way as to improve the possibility to change them later on. To code for flexibility.
[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

So for a list, to add a bullet, it first uses new to allocate a space, and then constructs the bullet object.
But for a vector, it constructs the bullet object right away because memory is already allocated.

The standard C++ containers use a pooled-allocation strategy. The don't use [font=courier new,courier,monospace]new[/font] and [font=courier new,courier,monospace]delete[/font] for memory allocation, they use a [font=courier new,courier,monospace]std::allocator[/font] object (which may or may not use [font=courier new,courier,monospace]new[/font] and [font=courier new,courier,monospace]delete[/font]) which will actually keep the memory hanging around after a contained object is freed. This allows better amortized performance as objects are added and removed from a list (or vectors are created and destroyed) at the expense of monotonically increasing memory usage over time. If you need to know which approach is best you need to measure.

If you don't have actual numbers from your performance analysis, you're not doing optimization so much as ritualized superstition.

Stephen M. Webb
Professional Free Software Developer

This topic is closed to new replies.

Advertisement