I noted it's been more than 2 years since my last post. I'd promise I won't wait as much for the next one, but it's a promise I can't make.
So, what do I want to talk about? Well, there is this talk given by Jonathan Blow. Yes it's a talk given at mid September and I did watch it within a week of it's release, so why talking about it now? Because I didn't have the inspiration before.
In the video he asked for comments to a email address. I intend to mail him this post, but I fear I am late, as he has already started to implement his ideas but I'd like to open this discussion anyway.
So... I'm going to say it, and I fear anyone knowing what we are talking about will stop when I say the cursed words, close the window, and never return, so I beg you to at least finish this paragraph. Here they go: a garbage collector is not a bad thing, even in games. Are you still here? Good. Now I'm gonna give a little of what you want to hear: the problem is not the garbage collector, the problem is forcing the programmer to use it.
Do I have your attention now? I hope so.
The key is something J.Blow say in it's talk: the problem are "Big Agenda" languages, like "a language without dangling pointers, nor memory leaks" (yes, GC'd languages still have memory leaks, and C# lets you have your dangling pointers, but you get the point). Another thing J.Blow says is he doesn't want tools that solve 100% of the problems, because there are problems that will just be illfitted fot those tools. So my point is... do not force GC, give it as an option.
How?
Memory Primitives
Now I'll start with some pointer stuff. Forget about GC, forget about J.Blow talk. What do I want to have in a language, memory wise? Build-in strong and weak pointers. Build-in as Objective-C, not as C++. C++ pointers are BOILERPLATE. They scream at you, HEY I'M A SHARED POINTER!!! No, I don't want that, I want a little modifier to our old "*", of 1 or 2 characters at most, so I can tell him, hey, I want you to make this pointer reference counted / weak or unique (I belive J.Blow put unique pointers in his language, so this step should not be that hard to do).I know the n1 critique this proposal will get: ref counting affects performance, it is slower! And it is. But do I care? I'll tell you what affects performance: cache misses. Adding/Substracting from an integer? Negligible. An integer wich is probably stored on the stack or near data you probably just used? Won't be a problem at all. Weak pointers have slightly more performance impact, but let's be honest, if I need smart pointers, I can program them, I just want them to be as transparent as possible, so they don't take up half my line of code because of std::shared_prt
More Memory Primitives
Last section was an introduction to this one. This is the meat of the post, this is where the fun is, so let's get the ingredients:- Strong references.
- Weak references
- Allocators
- A deallocate reference operation (optional... sometimes).
Now I have to prevent you. This features are suggested with the "good" programmer in mind. As Blow said, we want a language centered on good programmers, give them the tools to do their best, not tools so safe the get hard to use.
Now we'll need to add some rules to the language to make this work. First I'll talk about the allocators: they may be Garbage Collected, but they may be not. Allocators are used mainly to allocate. Any surprises here? Well, instead to have a good old "new" operator, we'll add a parameter to wathever alloc function our language uses to provide the allocator if we are creating a "reference" type. This may even be an instance function if we consider Allocators as objects in a OO language.
Then we'll add a deallocate function. It may or may not need the allocator, depending if we are able to store enought information in the reference to find the allocator "fast" enought. An important note is that this deallocate function is not allways mandatory to use, you will see why in the examples section.
Last consideration, what do we do if we have a reference type inside a structure that is allocated behind a reference. My suggestion is that it better be created with the same allocator, but I can find workarrounds to it that work... and maybe we can give some flexibility there, as a chance to shoot yourself to the feet if you are brave enough. It will have to give comprehensive error messages if someone screws up.
Examples
So, what is all this about? Will something usefull emerge from this? I say yes, at least for me.
First consider some kinds of Allocators we can provide:
- Linear allocators. Each time we allocate something it just moves a pointer. Then it has a "reset" operation, wich frees all created memory. We may even have a variant that calls all destructors on reset (slow), or one that works stack-like, marking points on the memory and only erasing to the last mark on reset. Of course you won't need to call deallocate to any object created by those, but you could do for debugging purposes, to see you have everything under control. Would you like to have this IN THE LANGUAGE?
- Stack allocators. Similar to the previous one, this does force you to call deallocate to reverse order as allocate. Not as fast deallocating as a linear, but equaly fast allocating.
- Double stack allocator. Now I'm just listing a random "allocators usefull for game developing" list on the internet.
- A garbage collector allocator. This is the one that will need assistance from the compiler. As long as you don't allocate things within your main loop, you are good. It may also be easier to fine-tune as than Java/C#/Other GCs, as you may know which threads won't need to be paralyzed by a garbage collection, you could even use it to your benefit forcing a GC while you are doing meaningfull work on another thread and your GC is cleaning/defragmenting things,