June 26, 2013
On 2013-06-26 01:16, Adam D. Ruppe wrote:

> You'd want it to be RAII or delegate based, so the scope is clear.
>
> with_allocator(my_alloc, {
>       do whatever here
> });
>
>
> or
>
> {
>     ChangeAllocator!my_alloc dummy;
>
>     do whatever here
> } // dummy's destructor ends the allocator scope
>
>
> I think the former is a bit nicer, since the dummy variable is a bit
> silly. We'd hope that delegate can be inlined.

It won't be inlined. You would need to make it a template parameter to have it inlined.

-- 
/Jacob Carlborg
June 26, 2013
Bloomberg released an STL alternative called BSL which contains an alternate allocator model. In a nutshell object supporting custom allocators can optionally take an allocator pointer as an argument. Containers will save the pointer and use it for all their allocations. It seems simple enough and does not embed the allocator in the type.

https://github.com/bloomberg/bsl/wiki/BDE-Allocator-model

On Tuesday, 25 June 2013 at 22:22:09 UTC, cybervadim wrote:
> I know Andrey mentioned he was going to work on Allocators a year ago. In DConf 2013 he described the problems he needs to solve with Allocators. But I wonder if I am missing the discussion around that - I tried searching this forum, found a few threads that was not actually a brain storm for Allocators design.
>
> Please point me in the right direction
> or
> is there a reason it is not discussed
> or
> should we open the discussion?
>
>
> The easiest approach for Allocators design I can imagine would be to let user specify which Allocator operator new should get the memory from (introducing a new keyword allocator). This gives a total control, but assumes user knows what he is doing.
>
> Example:
>
> CustomAllocator ca;
> allocator(ca) {
>   auto a = new A; // operator new will use ScopeAllocator::malloc()
>   auto b = new B;
>
>   free(a); // that should call ScopeAllocator::free()
>   // if free() is missing for allocated area, it is a user responsibility to make sure custom Allocator can handle that
> }
>
> By default allocator is the druntime using GC, free(a) does nothing for it.
>
>
> if some library defines its allocator (e.g. specialized container), there should be ability to:
> 1. override allocator
> 2. get access to the allocator used
>
> I understand that I spent 5 mins thinking about the way Allocators may look.
> My point is - if somebody is working on it, can you please share your ideas?
June 26, 2013
On Wednesday, 26 June 2013 at 13:16:25 UTC, Jason House wrote:
> Bloomberg released an STL alternative called BSL which contains an alternate allocator model. In a nutshell object supporting custom allocators can optionally take an allocator pointer as an argument. Containers will save the pointer and use it for all their allocations. It seems simple enough and does not embed the allocator in the type.
>
> https://github.com/bloomberg/bsl/wiki/BDE-Allocator-model

I think the problem with such approach is that you have to maniacally add support for custom allocator to every class if you want them to be on a custom allocator.
If we simply able to say - all memory allocated in this area {} should use my custom allocator, that would simplify the code and no need to change std lib.
The next step is to notify allocator when the memory should be released. But for the stack based allocator that is not required.
More over, if we introduce access to different GCs (e.g. mark-n-sweep, semi-copy, ref counted), we should be able to say this {} piece of code is my temporary, so use semi-copy GC, the other code is long lived and not much objects created, so use ref counted. That is, it is all runtime support and no need changing the library code.
June 26, 2013
26-Jun-2013 02:22, cybervadim пишет:
> I know Andrey mentioned he was going to work on Allocators a year ago.
> In DConf 2013 he described the problems he needs to solve with
> Allocators. But I wonder if I am missing the discussion around that - I
> tried searching this forum, found a few threads that was not actually a
> brain storm for Allocators design.
>
> Please point me in the right direction
> or
> is there a reason it is not discussed
> or
> should we open the discussion?
>
>
> The easiest approach for Allocators design I can imagine would be to let
> user specify which Allocator operator new should get the memory from
> (introducing a new keyword allocator). This gives a total control, but
> assumes user knows what he is doing.
>
> Example:
>
> CustomAllocator ca;
> allocator(ca) {
>    auto a = new A; // operator new will use ScopeAllocator::malloc()
>    auto b = new B;
>
>    free(a); // that should call ScopeAllocator::free()
>    // if free() is missing for allocated area, it is a user
> responsibility to make sure custom Allocator can handle that
> }

Awful. What that extra syntax had brought you? Except that now new is unsafe by design?
Other questions involve how does this allocation scope goes inside of functions, what is the mechanism of passing it up and down of call-stack.

Last but not least I fail to see how scoped allocators alone (as presented) solve even half of the problem.

-- 
Dmitry Olshansky
June 26, 2013
26-Jun-2013 14:03, Robert Schadek пишет:
> On 06/26/2013 12:50 AM, Adam D. Ruppe wrote:
>> On Tuesday, 25 June 2013 at 22:22:09 UTC, cybervadim wrote:
>>> (introducing a new keyword allocator)
>>
>> It would be easier to just pass an allocator object that provides the
>> necessary methods and don't use new at all. (I kinda wish new wasn't
>> in the language. It'd make this a little more consistent.)
>>
>
> I did think about this as well, but than I came up with something that
> IMHO is even simpler.
>
> Imagine we have two delegates:
>
> void* delegate(size_t);  // this one allocs
> void delegate(void*);    // this one frees
>
> you pass both to a function that constructs you object. The first is
> used for allocation the
> memory, the second gets attached to the TypeInfo and is used by the gc
> to free
> the object.

Then it's just GC but with an extra complication.

> This would be completely transparent to the user.
>
> The use in a container is similar. Just use the alloc delegate to
> construct the objects and
> attach the free delegate to the typeinfo. You could even mix allocator
> strategies in the middle
> of the lifetime of the container.
>


-- 
Dmitry Olshansky
June 26, 2013
On Wed, Jun 26, 2013 at 04:10:49PM +0200, cybervadim wrote:
> On Wednesday, 26 June 2013 at 13:16:25 UTC, Jason House wrote:
> >Bloomberg released an STL alternative called BSL which contains an alternate allocator model. In a nutshell object supporting custom allocators can optionally take an allocator pointer as an argument. Containers will save the pointer and use it for all their allocations. It seems simple enough and does not embed the allocator in the type.
> >
> >https://github.com/bloomberg/bsl/wiki/BDE-Allocator-model
> 
> I think the problem with such approach is that you have to maniacally add support for custom allocator to every class if you want them to be on a custom allocator.

Yeah, that's a major inconvenience with the C++ allocator model. There's no way to say "switch to allocator A within this block of code"; if you're given a binary-only library that doesn't support allocators, you're out of luck. And even if you have the source code, you have to manually modify every single line of code that performs allocation to take an additional parameter -- not a very feasible approach.


> If we simply able to say - all memory allocated in this area {}
> should use my custom allocator, that would simplify the code and no
> need to change std lib.
> The next step is to notify allocator when the memory should be
> released. But for the stack based allocator that is not required.
> More over, if we introduce access to different GCs (e.g.
> mark-n-sweep, semi-copy, ref counted), we should be able to say this
> {} piece of code is my temporary, so use semi-copy GC, the other
> code is long lived and not much objects created, so use ref counted.
> That is, it is all runtime support and no need changing the library
> code.

Yeah, I think the best approach would be one that doesn't require changing a whole mass of code to support. Also, one that doesn't require language changes would be far more likely to be accepted, as the core D devs are leery of adding yet more complications to the language.

That's why I proposed that gc_alloc and gc_free be made into thread-global function pointers, that can be swapped with a custom allocator's version. This doesn't have to be visible to user code; it can just be an implementation detail in std.allocator, for example. It allows us to implement custom allocators across a block of code that doesn't know (and doesn't need to know) what allocator will be used.


T

-- 
Fact is stranger than fiction.
June 26, 2013
On Wednesday, 26 June 2013 at 14:17:03 UTC, Dmitry Olshansky wrote:
> Awful. What that extra syntax had brought you? Except that now new is unsafe by design?
> Other questions involve how does this allocation scope goes inside of functions, what is the mechanism of passing it up and down of call-stack.
>
> Last but not least I fail to see how scoped allocators alone (as presented) solve even half of the problem.

Extra syntax allows me not touching the existing code.
Imagine you have a stateless event processing. That is event comes, you do some calculation, prepare the answer and send it back. It will look like:

void onEvent(Event event)
{
   process();
}

Because it is stateless, you know all the memory allocated during processing will not be required afterwards. So the syntax I suggested requires a very little change in code. process() may be implemented using std lib, doing several news and resizing.

With new syntax:


void onEvent(Event event)
{
   ScopedAllocator alloc;
   allocator(alloc) {
     process();
   }
}

So now you do not use GC for all that is created inside the process().
ScopedAllocator is a simple stack that will free all memory in one go.

It is up to the runtime implementation to make sure all memory that is allocated inside allocator{} scope is actually allocated using ScopedAllocator and not GC.

Does it make sense?
June 26, 2013
>> Imagine we have two delegates:
>>
>> void* delegate(size_t);  // this one allocs
>> void delegate(void*);    // this one frees
>>
>> you pass both to a function that constructs you object. The first is
>> used for allocation the
>> memory, the second gets attached to the TypeInfo and is used by the gc
>> to free
>> the object.
>
> Then it's just GC but with an extra complication.
>
IMHO, not really, as the place you get the memory from is not managed by
the GC, or at least not
directly. The GC algorithm would see that there is a "free delegate"
attached to the object and would
use this to free the memory.

The same should hold true for calling GC.free.

Or are you talking about ref counting and such?
June 26, 2013
On Wednesday, 26 June 2013 at 14:26:03 UTC, H. S. Teoh wrote:
> Yeah, I think the best approach would be one that doesn't require
> changing a whole mass of code to support. Also, one that doesn't require
> language changes would be far more likely to be accepted, as the core D
> devs are leery of adding yet more complications to the language.
>
> That's why I proposed that gc_alloc and gc_free be made into
> thread-global function pointers, that can be swapped with a custom
> allocator's version. This doesn't have to be visible to user code; it
> can just be an implementation detail in std.allocator, for example. It
> allows us to implement custom allocators across a block of code that
> doesn't know (and doesn't need to know) what allocator will be used.
>

Yes, being able to change gc_alloc, gc_free would do the work. If runtime  remembers the stack of gc_alloc/gc_free functions like pushd, popd, that would simplify its usage.
I think this is a very nice and simple solution to the problem.

June 26, 2013
26-Jun-2013 03:16, Adam D. Ruppe пишет:
> On Tuesday, 25 June 2013 at 22:50:55 UTC, H. S. Teoh wrote:
>> And maybe (b) can be implemented by making gc_alloc / gc_free
>> overridable function pointers? Then we can override their values and
>> use scope guards to revert them back to the values they were before.
>
> Yea, I was thinking this might be a way to go. You'd have a global
> (well, thread-local) allocator instance that can be set and reset
> through stack calls.
>
> You'd want it to be RAII or delegate based, so the scope is clear.
>
> with_allocator(my_alloc, {
>       do whatever here
> });
>
>
> or
>
> {
>     ChangeAllocator!my_alloc dummy;
>
>     do whatever here
> } // dummy's destructor ends the allocator scope
>

Both suffer from
a) being totally unsafe and in fact bug prone since all references obtained in there are now dangling (and there is no indication where they came from)
b) imagine you need to use an allocator for a stateful object. Say forward range of some other ranges (e.g. std.regex) both scoped/stacked to allocate its internal stuff. 2nd one may handle it but not the 1st one.
c) transfer of objects allocated differently up the call graph (scope graph?), is pretty much neglected I see.

I kind of wondering how our knowledgeable community has come to this.
(must have been starving w/o allocators way too long)

> {
>     malloced_string str;
>     auto got = to!string(10, str);
> } // str is out of scope, so it gets free()'d. unsafe though: if you
> stored a copy of got somewhere, it is now a pointer to freed memory. I'd
> kinda like language support of some sort to help mitigate that though,
> like being a borrowed pointer that isn't allowed to be stored, but
> that's another discussion.
>
In contrast 'container as an output range' works both safely and would be still customizable.

IMHO the only place for allocators is in containers other kinds of code may just ignore allocators completely.

std.algorithm and friends should imho be customized on 2 things only:

a) containers to use (instead of array)
b) optionally a memory source (or allocator) f container is temporary(scoped) to tie its life-time to smth.

Want temporary stuff? Use temporary arrays, hashmaps and whatnot i.e. types tailored for a particular use case (e.g. with a temporary/scoped allocator in mind).
These would all be unsafe though. Alternative is ref-counting pointers to an allocator. With word on street about ARC it could be nice direction to pursue.

Allocators (as Andrei points out in his video) have many kinds:
a) persistence: infinite, manual, scoped
b) size: unlimited vs fixed
c) block-size: any, fixed, or *any* up to some maximum size

Most of these ARE NOT interchangeable!
Yet some are composable however I'd argue that allocators are not composable but have some reusable parts that in turn are composable.

Code would have to cutter for specific flavors of allocators still so we'd better reduce this problem to the selection of containers.

-- 
Dmitry Olshansky