Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


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


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 lride   Members   -  Reputation: 633

Like
0Likes
Like

Posted 06 November 2012 - 08:14 PM

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.

Sponsor:

#2 zacaj   Members   -  Reputation: 643

Like
2Likes
Like

Posted 06 November 2012 - 08:21 PM

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

#3 lride   Members   -  Reputation: 633

Like
0Likes
Like

Posted 06 November 2012 - 08:43 PM

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.

#4 phantom   Moderators   -  Reputation: 7593

Like
1Likes
Like

Posted 06 November 2012 - 08:52 PM

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;

#5 Servant of the Lord   Crossbones+   -  Reputation: 21185

Like
1Likes
Like

Posted 06 November 2012 - 08:56 PM

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();
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#6 lride   Members   -  Reputation: 633

Like
0Likes
Like

Posted 06 November 2012 - 09:08 PM

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.

#7 Servant of the Lord   Crossbones+   -  Reputation: 21185

Like
1Likes
Like

Posted 06 November 2012 - 09:59 PM

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.
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#8 lride   Members   -  Reputation: 633

Like
0Likes
Like

Posted 07 November 2012 - 06:42 AM

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?

Edited by lride, 07 November 2012 - 06:53 AM.

An invisible text.

#9 larspensjo   Members   -  Reputation: 1557

Like
0Likes
Like

Posted 07 November 2012 - 07:43 AM

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.
Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#10 Bregma   Crossbones+   -  Reputation: 5498

Like
1Likes
Like

Posted 07 November 2012 - 08:54 AM

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 new and delete for memory allocation, they use a std::allocator object (which may or may not use new and delete) 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

#11 Aliii   Members   -  Reputation: 1448

Like
0Likes
Like

Posted 07 November 2012 - 09:28 AM

An idea:
You allocate the array
Bullets **bullets= new Bullets*[200]

Then you make 2 lists:
list<Bullets*> dead_bullets, alive_bullets

....first all bullets(pointers) are in the dead_bullets and when a bullet is needed you put it into the alive_bullets and remove it from dead_bullets.

edit:
of course you fill the array first:)
for(...)
bullets[i]= new Bullet()

Edited by Aliii, 07 November 2012 - 09:31 AM.


#12 Servant of the Lord   Crossbones+   -  Reputation: 21185

Like
0Likes
Like

Posted 07 November 2012 - 10:27 AM

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?

Basically, yes. However, if vector runs out of free allocated memory, it'll have to get a new chunk of memory, and move every element to the new chunk of memory.
Vector holds all its elements in one continuous block of memory (faster traversals, slower insertion), list holds each element in their own block of memory (longer traversals, faster insertion).

If you know in advance how many elements you'll have the vector hold, you can call vector.reserve() and then push_back() and pop_back() without fear of reallocation causing an unexpected slowdown. By "slower" and "faster" we're talking about time periods that all less than 1/100,000th of a second - unless you are really hitting everything tens of thousands of times a frame, these kinds of "which is faster in what situation" issues can mostly be ignored until you find yourself actually needing the extra speed.
It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#13 shadowomf   Members   -  Reputation: 323

Like
0Likes
Like

Posted 07 November 2012 - 11:42 AM

If you have other objects with a short live time (per frame, per level, ...) you could also take a look at region based memory management.
I would guess for bullets it's not that optimal, but for other objects that have a predictable life time it's a simple way to prevent memory leaks.

#14 SiCrane   Moderators   -  Reputation: 9675

Like
0Likes
Like

Posted 07 November 2012 - 02:23 PM

The standard C++ containers use a pooled-allocation strategy.

Not necessarily. The standard allows pooled allocation, but doesn't require it.

The don't use new and delete for memory allocation, they use a std::allocator object (which may or may not use new and delete) which will actually keep the memory hanging around after a contained object is freed.

Actually std::allocator itself is required to use new to allocate memory, though how often it is used (e.g. if it is called with every call to allocate()) is left unspecified. Other allocators may or may not use new.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS