Does diddy have an Object Pooler?

Monkey Forums/Monkey Programming/Does diddy have an Object Pooler?

Amon(Posted 2012) [#1]
If so, how do I use it? I'm constantly wondering how to deal with large amounts of object creation.

Samah/therevills: if diddy does not have something like this can one be implemented and tutoriaslised, please?

Thank You!


Samah(Posted 2012) [#2]
I implemented one for my MonkeyTouch project, and I'm planning on generalising it and adding it to Diddy.

Given the way it works, what will probably happen is that you'll need to make your class implement IPoolable and then implement a property that returns whether the item is active (basically just store a boolean field).
The pool will purge any inactive items, and shuffle the rest around so that the active objects will always be sequential (even if they're unordered). This way, an enumerator will not need to skip over inactive items.

Should be soon, hopefully!


Amon(Posted 2012) [#3]
That's good news, Samah! Looking forward to the update!


Samah(Posted 2012) [#4]
Done, go break it. ;)

[monkeycode]Class Foo Implements IPoolable
Private
' your own field, since IPoolable is an interface
Field activeInPool:Bool = False

Public
' the pool reads this property when purging
Method ActiveInPool:Bool() Property
Return activeInPool
End

' you should write to this property before purging
Method ActiveInPool:Void(activeInPool:Bool) Property
Self.activeInPool = activeInPool
End

' called by GetObject()
Method InitFromPool:Void(arg:Object = Null)
' optionally do stuff with arg
End

' called when the object is purged
Method PurgedFromPool:Void()
End
End

' create the pool, with 1000 instances of Foo
Local pool:Pool<Foo> = New Pool<Foo>(1000)
' get one object
Local hello:Foo = pool.GetObject() ' optionally pass an arg here
' do some stuff with hello
' finished with that instance, set active to false
hello.ActiveInPool = False
' object will be purged on the next Sort() or EachIn
' or you can manually purge with pool.Purge()[/monkeycode]

Creating a pool will instantiate a bunch of objects automatically. Call GetObject() to get one instance, which increases pool.ActiveCount. If the pool is full, it will return Null.

When a purge is called, it shuffles object references such that the active instances are contiguous from 0 until ActiveCount, then updates ActiveCount. An EachIn will first call a purge. Objects can be retrieved during an EachIn as it will not affect the current loop, but a purge will throw a ConcurrentModificationException if any objects were purged. This is to prevent objects from being looped twice, or skipped entirely.

To use the sorting feature, you will need to either create a Comparator, or implement IComparable. They work the same way as sorting for ArrayList.


Amon(Posted 2012) [#5]
You are of course beautiful! :)


Samah(Posted 2012) [#6]
So have you broken it yet? ;)
I'll make an example once I've got this MonkeyTouch stuff out of the way (I'm way past my due date...)


Amon(Posted 2012) [#7]
Samah, how would I integrate this with a bullet class? I've tried, it compiles and does not throw errors but my implementation is wrong somewhere as nothing seems to show up while everything else does.

Oh! Samah! Are you the Sama.Van from the Unity Boards? 3d Artist like I've never seen?


Samah(Posted 2012) [#8]
Oh! Samah! Are you the Sama.Van from the Unity Boards? 3d Artist like I've never seen?

Nope, though I wish I had artistic skills... XD

Can you show me a snippet of your code?

Here's an example (I haven't tested it):
[monkeycode]' define pool
Field bulletPool:Pool<Bullet> = New Pool<Bullet>(1000)

' add some test bullets (normally you'd add one of these when something fires)
For Local i:Int = 0 Until 100
Local bullet:Bullet = bulletPool.GetObject()
If bullet Then
bullet.image = someImage
bullet.x = Rnd(0, DeviceWidth())
bullet.y = Rnd(0, DeviceHeight())
End
Next

' render bullets (EachIn only loops on active bullets, not all 1000)
For Local bullet:Bullet = EachIn(bulletPool)
bullet.Render()
Next

Class Bullet Implements IPoolable
Private
Field activeInPool:Bool

Public
Field x#, y#
Field image:Image

Method ActiveInPool:Bool() Property
Return activeInPool
End

Method ActiveInPool:Void(activeInPool:Bool) Property
Self.activeInPool = activeInPool
End

Method InitFromPool:Void(arg:Object = Null)
End

Method PurgedFromPool:Void()
End

Method Render:Void()
DrawImage(image, x, y)
End
End[/monkeycode]


marksibly(Posted 2012) [#9]
Hi,

If all you want is a way to create/destroy objects, something like this might be enough:

Class Pool<T>

	Method New( initialCapacity:Int=10 )
		For Local i:=0 Until initialCapacity
			_pool.Push New T
		Next
	End

	Method Allocate:T()
		If _pool.IsEmpty() Return New T
		Return _pool.Pop()
	End
	
	Method Free:Void( t:T )
		_pool.Push t
	End
	
	Private
	
	Field _pool:=New Stack<T>

End


Sample usage...

Global bulletPool:=New Pool<Bullet>( 1000 )

Function CreateBullet:Bullet( x,y,etc )
   Local bullet:=bulletPool.Allocate()
   bullet.Create x,y,etc
   Return bullet
End

Function DestroyBullet:Void( bullet:Bullet )
   bullet.Destroy
   bulletPool.Free bullet
end



Samah(Posted 2012) [#10]
A simple pool like you've described is fine if all you want to do is reuse objects, but generally with a Bullet class you would want to loop through them so that you can call Update and/or Render on them. That means you would need to store those Allocate()d references in a list or array somewhere else.

The point of the Diddy Pool class is to allow reuse of objects, and to keep their order sequential without having to do any array shifting when one of the elements becomes inactive.


marksibly(Posted 2012) [#11]
I'd be inclined to use a separate container for 'active' bullets etc - keeps memory management completely separate, and removes the need for the activeInPool stuff.


Samah(Posted 2012) [#12]
What kind of container though? If you use a List you have object manipulation (nodes), if you use a Stack or an array, you'll need to mess around with loops when removing items from the middle of the container.


Amon(Posted 2012) [#13]
Thanks for your example, Samah. I have got it working nice. On a side note are the particles in the particle system pooled also or are they mobile efficient already as they are?


Samah(Posted 2012) [#14]
The particle system doesn't have a Particle class, it uses parallel float arrays. In my experience in writing particle systems, I've found that 20 arrays of 10000 elements is generally faster and more memory efficient than 10000 objects of 20 fields each.


Amon(Posted 2012) [#15]
Thanks for the info! Just tried blasting it with 100's of particles and it holds its own great. :)