Implementing a Garbage Collector for AngelScript

Started by
34 comments, last by WitchLord 18 years, 11 months ago
Quote:Original post by WitchLord
elScript already allows the applications to register their own memory allocation routines for registered object types. Take a look at the behaviours asBEHAVE_ALLOC and asBEHAVE_FREE. These were implemented in AngelScript 2.1.0.
;)


Dang.. I never noticed the new behaviours in any of the WIP's :) That's a good thing to have! I'll probably add some functionality to my script manager to enable memory pooling based on this.

I'm all for implementing a GC. So will this mean that we'll have a 'new' operator to differentiate from static allocation? Also, am I correct to assume that objects created in this manner will be referred by handle?
tIDE Tile Map Editorhttp://tide.codeplex.com
Advertisement
Actually AngelScript doesn't do any static allocation, so there is no need for a 'new' operator. Even local objects declared in a function are dynamically allocated and stored by reference (or handle). Thus if an object supports handles it can be moved outside the function, without risking it being deallocated as the function returns. Objects that don't support handles (primitives as well) doesn't allow it's reference to be stored so they will be safely released once the function returns.


AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Quote:Original post by WitchLord
After reading the document about Garbage Collection for Python, I have to say that their way is looking very attractive.

Also take a look at the Parrot link I posted. They don't like reference counting very much and it's always worth to read conflicting opinions. I disagree with their statements though, if you're interested in my arguments let me know and I'll post more.

Keep in mind that if you go with the same approach Python developers used, you can do *a lot* better than Python because all information is available to you at compile time (which is not true for Python). In particular, their developers isolate objects that *may* have cyclic references (for Python that means all containers, classes, etc.) and run cyclic reference queries on those objects. For AS this process can be optimized because you can determine the exact set of objects that do have cyclic references at compile time. That means you'll only have to run the cyclic reference collection mechanism on a very small set of objects (only those that actually have cyclic references, not all objects that *may* have them like in Python). This may not seem like much but if you give it some thought, it's an enormous optimization.
I read the document on Parrot's solution. I can understand their arguments, but I don't fully agree with them either.

Sure, tell me your arguments as to why reference counting is better, just keep it objective ;)

And I agree with you about Python's way of doing it. I would only add this type of cyclic verification for the structures that really can have cyclic references.

I read about Ruby's garbage collector as well. It is basically a mark and sweep algorithm, that also verifies the C stack and registered C global variables for potential references. It works much like Hans Boehms GC in that it assumes that any word can be a pointer (at least if the 2 least significant bits are 0). What I don't like about this method is that it requires the GC to have complete knowledge over where the application may store references, including any dynamic structures that are allocated by the application. So Ruby's way of doing it is out of the question.

By the way, do you think Python's way of doing it can be incremental? I think so, but I haven't had the time to analyze it sufficiently to prove it to myself.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Quote:
Every single place where an object is referenced, and every single place where a reference is dropped, must properly alter the refcount of the objects being manipulated. One mistake and an object (and everything it references, directly or indirectly) lives forever or dies prematurely. Since a lot of code references objects, that's a lot of places to scatter reference counting code.

I'm not even sure what this means. You write a wrapper once and forget about it.
Quote:
The cost of a reference counting scheme is directly linked to the number of times code references, or unreferences, objects.

The cost involves a single pointer dereference and increment/decrement. Considering that languages like Python look up symbols in a dictionary every time they're referenced, this is minimal. It really boils down to your priorities. If you want to offload the cost to a single long running step, you use mark and sweep. If you like an incremental approach that scatters a minimal cost accross the whole application, you use reference counting.
Quote:
The data is also less dense, as there has to be a reference count embedded in it. Once again, that means more cache used for each object during normal running, and lower cache density.

Considering that modern operating systems run many processes at once, this hardly matters. The only time where this may make a difference is in very tight inner loops. These places require special handling anyway, people don't tend to write very tight loops in dynamic languages. Yet again, we're dealing with two assembly instructions here, hardly a strain on the cache.
Quote:Original post by WitchLord
By the way, do you think Python's way of doing it can be incremental? I think so, but I haven't had the time to analyze it sufficiently to prove it to myself.

Well, Python can't really afford to do it fully incrementally because if they checked for cyclic references for all potential objects, 80% of the time will end up doing cyclic reference checks. In AS you'll know the exact set of objects you'll need to check (which in real applications will be minimal), which means you don't need to add them to the list and check later. You can just walk the graph immediately.
I'd just like to mention that I have the GC working already, though obviously not in any way optimized. It took me about 2 hours to implement once I decided which way to go. I need to make some other changes before releasing the next WIP though, because right now it is very easy to crash the GC if the calls are made in the wrong order.

I don't think I will make the GC incremental for the next WIP, but don't worry it will be incremental for the final version.

I decided to go with the same technique used by Python, as it was the easiest to implement and test. Trying to get a full mark-and-sweep GC working with AngelScript will require a lot of work as AS' memory management isn't organized in a well-orderly manner, e.g. objects are allocated on the normal C++ heap, and the context stacks can be divided in multiple parts as they have the ability to grow dynamically. But maybe in the future I'll give it try.

In most cases I really don't think anyone will notice a difference in performance between this GC and any other. The important thing is that the script execution speed is more or less constant so that the application can predict execution times. After all, AngelScript is mostly used for games, and they cannot afford the application to pause while the script engine collects garbage.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement