About Monkey 2 › Forums › Monkey 2 Programming Help › Memory Pool – Manual memory management
This topic contains 7 replies, has 3 voices, and was last updated by
ivan.velho 2 years, 6 months ago.
-
AuthorPosts
-
October 3, 2016 at 10:12 pm #4254
Is there a way to manually manage memory, like Pool in Monkey 1 ?
Thanks
October 3, 2016 at 11:56 pm #4255IIRC, there is no Monkey1-like Pool class in MX2’s collections library.
I did some pooling experiments several MX2 releases back. I could not find a solution that performed quicker than run-time Newing of objects, regardless of the quantity (on PC hardware). Memory fragmentation issues, however, are theoretically solved by object pooling.
Here’s a simple generic object pooling class:
Note that any objects added to it require a default New() constructor.
[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170[crayon-5cba9b9e6230e135407959 inline="true" ]#import "<std>"Using std.collections'----Class Pool<T>PrivateField _autoResize:BoolField _autoResizeMultiplier:FloatField _capacity:UIntField _objs:Stack<T>Method Fill:Void()For Local n:= _objs.Length Until _capacity'A default constructor (New() without parameters) is required'otherwise the compiler will raise the following error:'Can't find overload for 'new(...)' with argument types ()_objs.Push(New T())NextEndPublicMethod New(capacity:UInt = 10, autoResize:Bool = True, autoResizeMultiplier:Float = 2.0)_objs = New Stack<T>Capacity = capacityAutoResize = autoResizeAutoResizeMultiplier = autoResizeMultiplierEndProperty AutoResize:Bool()Return _autoResizeSetter (value:Bool)_autoResize = valueEndProperty AutoResizeMultiplier:Float()Return _autoResizeMultiplierSetter (value:Float)_autoResizeMultiplier = valueIf _autoResizeMultiplier < 1.1 Then _autoResizeMultiplier = 1.1EndProperty Capacity:UInt()Return _capacitySetter (value:UInt)If value >= _capacity_capacity = valueFill()Else_capacity = valueFor Local n := _objs.Length until _capacity Step - 1_objs.Pop()NextEndifEndProperty Contents:Stack<T>()Return _objsEndMethod Put:Void(obj:T, forceAppend:Bool = False)If _objs.Length < _capacity_objs.Push(obj)ElseIf forceAppend_objs.Push(obj)_capacity += 1EndifEndifEndMethod Get:T()If _objs.EmptyIf AutoResizeCapacity *= AutoResizeMultiplierElseReturn NullEndifEndifReturn _objs.Pop()EndEnd'--------------------Class ObjPrivateGlobal IdCounter:UInt = 1Field _id:UIntPublicMethod New()_id = IdCounterIdCounter += 1EndMethod ToString:string()Return "ID: " + _idEndFunction PrintStack:Void(objs:Stack<Obj>)If objs = Null Then ReturnFor Local obj := Eachin objsPrint obj.ToString()NextEndEnd'--------------------Function Main:Void()Local pool:=New Pool<Obj>(5)Obj.PrintStack(pool.Contents)'pool.Capacity = 15'Obj.PrintStack(pool.Contents)Local active := New Stack<Obj>Print "~n------------"Print "ALLOCATION"Print "------------"Print "~nActive:"For Local n:= 1 To 6Local obj := Cast<Obj>(pool.Get())If (obj<>Null)active.Push(obj)ElsePrint "NULL"EndifNextObj.PrintStack(active)Print "~nPooled:"Obj.PrintStack(pool.Contents)Print "~n------------"Print "FREEING"Print "------------"For Local n := 1 To 3pool.Put(active.Pop(), True)NextPrint "~nActive:"Obj.PrintStack(active)Print "~nPooled:"Obj.PrintStack(pool.Contents)EndI do have another solution somewhere that makes use of objects that implement a “Pooleable” Interface. I’ll see if I can dig it up and post it as another example…
October 4, 2016 at 12:20 am #4256Here’s the other pooling technique I experimented with:
[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232[crayon-5cba9b9e6903a025171996 inline="true" ]#import "<std>"Using std.collectionsInterface iPoolableField idx:Int'*** For TESTING only.Method ToString:String()'***EndClass ArrayPoolPrivateField _capacity:IntField _objs:iPoolable[]Field _autoResize:BoolField _autoResizeMultiplier:FloatField _creator:iPoolable()Field _idxAvailable:IntPublicMethod New(creator:iPoolable(), capacity:Int = 10, autoResize:bool = True, autoResizeMultiplier:Float = 2.0)AutoResize = autoResizeAutoResizeMultiplier = autoResizeMultiplierCreator = creatorCapacity = capacity_idxAvailable = 0EndProperty AutoResize:Bool()Return _autoResizeSetter (value:Bool)_autoResize = valueEndProperty AutoResizeMultiplier:Float()Return _autoResizeMultiplierSetter (value:Float)If value <= 1.0 Then value = 1.1_autoResizeMultiplier = valueEndProperty Capacity:Int()Return _capacitySetter (value:Int)If value >= _capacityLocal arr := New iPoolable[value]_objs.CopyTo(arr, 0, 0, _objs.Length)_objs = arrFor Local i:= _capacity Until value_objs[i] = _creator()_objs[i].idx = iNextFindAvailableIdx()Else_objs = _objs.Slice(0, value)If _objs[value] = Null Then _idxAvailable = valueEndif_capacity = valueEndProperty Creator:Void(func:iPoolable())If func = Null Then RuntimeError("Error: Poolable object creation function cannot be null.")_creator = funcEndProperty Contents:iPoolable[]()Return _objsEndMethod FindAvailableIdx:Void()For Local i:Int = 0 Until _objs.LengthIf _objs[i] <> Null_idxAvailable = iReturnEndifNext_idxAvailable = _capacityEndMethod Free:Void(obj:iPoolable)If obj = Null Then Return_idxAvailable -= 1_objs[_idxAvailable] = objEndMethod Allocate:iPoolable()If _idxAvailable >= _capacityIf Not _autoResizeReturn NullElseCapacity *= _autoResizeMultiplierEndifEndifLocal obj := _objs[_idxAvailable]_objs[_idxAvailable] = Null_idxAvailable += 1Return objEnd'*** For TESTINGMethod PrintAll:Void()Print "------"For Local obj := Eachin _objsIf obj = Null Then ContinuePrint obj.ToString() + " -- IDX = " + obj.idxNextPrint "------"End'***End'---------------Class Obj Implements iPoolablePrivateGlobal IdCounter:UInt = 1Field _id:UIntPublicFunction Create:iPoolable()Return New Obj()EndMethod New()_id = IdCounterIdCounter += 1EndMethod ToString:string()Return "ID: " + _idEnd'*** For TESTING only.Function PrintStack:Void(stack:Stack<Obj>)If stack = Null Then ReturnIf stack.Empty Then ReturnFor Local obj:= Eachin stackPrint obj.ToString()NextEnd'***End'---------------Function Main:Void()Local pool:=New ArrayPool(Obj.Create, 5, True)Local active := New Stack<Obj>Print "~n------------"Print "ALLOCATION"Print "------------"Print "~nActive:"For Local n:= 1 To 9Local obj := Cast<Obj>(pool.Allocate())If (obj<>Null)active.Push(obj)ElsePrint "NULL"EndifNextObj.PrintStack(active)Print "~nPooled:"pool.PrintAll()Print "~n------------"Print "FREE"Print "------------"For Local n := 1 To 5pool.Free(active.Pop())NextPrint "~nActive:"Obj.PrintStack(active)Print "~nPooled:"pool.PrintAll()Print "~nAllocate again"For Local n:= 1 To 4Local obj := Cast<Obj>(pool.Allocate())If (obj<>Null)active.Push(obj)ElsePrint "NULL"EndifNextPrint "~nActive:"Obj.PrintStack(active)Print "~nPooled:"pool.PrintAll()EndObjects to be pooled must implement the iPoolable Interface. The pool also requires a “creator function” (basically a constructor replacement).
This technique affords greater flexibility though at a cost of greater abstraction.
I used an array instead of a stack for performance testing reasons – surprisingly there was no performance benefit. Maybe I should reimplement it with a Stack…
October 4, 2016 at 2:05 am #4259My problem is free memory in time the destruction method is called, same problem every garbage collector has.
But if you want to be faster than a constructor method, I suggest you to create a pool of objects with a recycle method, this method must do the same as a constructor but it will do on an allocated object.
This is the reason you can’t be faster with any other strategy.October 4, 2016 at 2:10 am #4260The limitation of this method is the pool must contain only objects of the same class.
You cannot do up cast as down cast.October 4, 2016 at 2:21 am #4261I created a simple generic pool class. I think t’s as fast as it can get. it has no limitation on size and has a return to pool function. The only requirements is that all objects to be stored or created must extend StoreObject class:
[/crayon]Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859[crayon-5cba9b9e74894284776390 inline="true" ]'*******************************************************'* Pool *'*******************************************************Class Store<T>Field last:TField total:IntMethod New()End MethodMethod New(count:Int)Fill(count)End MethodMethod Fill:Void(total:Int)For Local i:Int = 0 Until totalLocal c:T = New Tc._pred = lastlast = cNextSelf.total += totalEnd MethodMethod Count:Int()Return totalEnd MethodMethod GetItem:T()If lastLocal c:T = lastlast = Cast<T>(last._pred)c._pred = Nulltotal -= 1Return cEndifReturn New TEnd MethodMethod ReturnItem:Void(obj:T)obj._succ = Nullobj._pred = lastlast = objtotal += 1End MethodEnd ClassClass StoreObject AbstractField _pred:StoreObjectMethod New()End methodEnd ClassOctober 5, 2016 at 5:45 am #4270I think I could use another approach to the problem.
I can use SDL/libc to allocate SDL pixmaps.
These are the major resources , I must study this solution . I believe monkey2 is based on SDL and the low level SDL/libc were provided to give a low level control.
I think I must study the integration and figure out how to do this .
Thanks guys for the help.
October 5, 2016 at 5:57 am #4271There is no problem:
Namespace std.graphics
#rem monkeydoc Pixmaps allow you to store and manipulate rectangular blocks of pixel data.
A pixmap contains a block of memory used to store a rectangular array of pixels.
#end
Class Pixmap#rem monkeydoc @hidden
#end
Field OnDiscarded:Void()#rem monkeydoc Creates a new pixmap.
@param width The width of the pixmap in pixels.
@param height The height of the pixmap in pixels.
@param format The pixmap format.
@param data A pointer to the pixmap data.
@param pitch The pitch of the data.
#end
Method New( width:Int,height:Int,format:PixelFormat=PixelFormat.RGBA32 )Local depth:=PixelFormatDepth( format )
Local pitch:=width*depth
Local data:=Cast<UByte Ptr>( libc.malloc( pitch*height ) )_width=width
_height=height
_format=format
_depth=depth
_data=data
_pitch=pitch
OnDiscarded=Lambda()
libc.free( data )
End
EndPixmap calls libc.free(data)
I believe it is not in garbage collector at all.
I believe Mark used the approach from libgdx (LWJGL)and allocate and delete resources out of garbage collector, through native method.
-
AuthorPosts
You must be logged in to reply to this topic.