On 9/22/13 6:35 PM, Manu wrote:Oxford comma please :o)
Well it looks like a good start, but the thing I'm left wondering after
reading this is still... how is it actually used?
I think the greatest challenge is finding a simple, clean
My design makes it very easy to experiment by allowing one to define complex allocators out of a few simple building blocks. It is not a general-purpose allocator, but it allows one to define any number of such.
and correct
way to actually specify which allocator should be used for making
allocations throughout your code, and perhaps more troublesome; within
generic code, and then layers of generic code.
No, or at least not at this level.Are you intending to be able to associate a particular allocator with a
class declaration?
Same answer.
What about a struct declaration?
The proposed design makes it easy to create allocator objects. How they are used and combined is left to the application.
What about a region of code (ie, a call tree/branch).
What if the given allocator should be used for most of the tree, except
for a couple of things beneath that always want to use their explicit
allocator?
An allocator instance is a variable like any other. So you use the classic techniques (shared globals, thread-local globals, passing around as parameter) for using the same allocator object from multiple places.What if I want to associate an allocator instance, not just an allocator
type (ie, I don't want multiple instances of the same type(/s) of
allocators in my code, how are they shared?
No, each allocator has its own means of dealing with memory. One could define a tracing allocator independent of the global GC.It wasn't clear to me from your demonstration, but 'collect()' implies
that GC becomes allocator-aware; how does that work?
No destructors are called at this level. Again, all these allocators understand is ubyte[].deallocateAll() and collect() may each free a whole lot of memory, but
it seems to me that they may not actually be aware of the individual
allocations they are freeing; how do the appropriate destructors for the
sub-allocations get called?
No, it's just that at this level some of these questions don't even have an appropriate answer - like we discuss atoms and molecules and you ask about building floors and beams and pillars.I have a suspicion you're going to answer most of these questions with
the concept of allocator layering, but I just don't completely see it.
I don't understand this.It's quite an additional burden of resources and management to manage
the individual allocations with a range allocator above what is supposed
to be a performance critical allocator to begin with.
Agreed. I've seen some uses of it that quite fall within the notion of the proverbial exception that prove the rule.C++'s design seems reasonable in some ways, but history has demonstrated
that it's a total failure, which is almost never actually used (I've
certainly never seen anyone use it).
I implemented the finite size for a freelist.Some allocators that I use regularly to think about:
A ring-buffer:
* Like a region allocator I guess, but the circular nature adds some
minor details, and requires the user to mark the heap from time to time,
freeing all allocations between the old mark and the new mark.
A pool:
* Same as a free-list, but with a maximum size, ie, finite pool of
objects pre-allocated and pre-populating the freelist.
I implemented that as well, it's one of the best designs I've defined in my life.
A pool-group:
* Allocate from a group of pools allocating differently sized
objects. (this is a good test for allocator layering, supporting a group
of pools above, and fallback to the malloc heap for large objects)