September 30, 2014
Am Mon, 29 Sep 2014 15:11:26 -0700
schrieb Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>:

> On 9/29/14, 10:25 AM, Jacob Carlborg wrote:
> > How does allocators fit in this? Will it be an additional argument to the function. Or a separate stack that one can push and pop allocators to?
> 
> There would be one allocator per thread (changeable) deferring to a global interlocked allocator. Most algorithms would just use whatever allocator is installed.
> 
> I know the notion of a thread-local and then global allocator is liable to cause some an apoplexy attack. But it's time to model things as they are - memory is a global resource and it ought to be treated as such. No need to pass allocators around except for special cases.
> 
> 
> Andrei
> 

> No need to pass allocators around except for special
> cases.

So you propose RC + global/thread local allocators as the solution for all memory related problems as 'memory management is not allocation'. And you claim that using output ranges / providing buffers / allocators is not an option because it only works in some special cases?

What if I don't want automated memory _management_? What if I want a function to use a stack buffer? Or if I want to free manually?

If I want std.string.toStringz to put the result into a temporary stack buffer your solution doesn't help at all. Passing an ouput range, allocator or buffer would all solve this.
September 30, 2014
On Tuesday, 30 September 2014 at 08:34:26 UTC, Johannes Pfau wrote:
> What if I don't want automated memory _management_? What if I want a
> function to use a stack buffer? Or if I want to free manually?

Agreed. This is the common case we need to solve for, but this is memory allocation, not management. I'm not sure where manual management fits into Andrei's scheme. Andrei, could you give an example of, e.g. how toStringz would work with a stack buffer in your proposed scheme?

Another thought: if we use a template parameter, what's the story for virtual functions (e.g. Object.toString)? They can't be templated.

September 30, 2014
On Tuesday, 30 September 2014 at 08:34:26 UTC, Johannes Pfau wrote:
> What if I don't want automated memory _management_? What if I want a
> function to use a stack buffer? Or if I want to free manually?
>
> If I want std.string.toStringz to put the result into a temporary stack
> buffer your solution doesn't help at all. Passing an ouput range,
> allocator or buffer would all solve this.

I don't understand, why wouldn't you be able to temporarily set the thread-local allocator to use the stack buffer, and restore it once done?
September 30, 2014
Am Mon, 29 Sep 2014 15:04:03 -0700
schrieb Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>:

> On 9/29/14, 10:16 AM, Paulo Pinto wrote:
> > Personally, I would go just for (b) with compiler support for increment/decrement removal, as I think it will be too complex having to support everything and this will complicate all libraries.
> 
> Compiler already knows (after inlining) that ++i and --i cancel each other, so we should be in good shape there. -- Andrei

That helps with very small, inlined functions until Marc
Schütz's work on borrowed pointers makes it redundant by
unifying scoped copies of GC, RC and stack pointers.
In any case inc/dec elision is an optimization and and not an
enabling feature. It sure is on the radar and can be improved
later on.

-- 
Marco

September 30, 2014
Am Tue, 30 Sep 2014 10:47:54 +0000
schrieb "Vladimir Panteleev" <vladimir@thecybershadow.net>:

> On Tuesday, 30 September 2014 at 08:34:26 UTC, Johannes Pfau wrote:
> > What if I don't want automated memory _management_? What if I
> > want a
> > function to use a stack buffer? Or if I want to free manually?
> >
> > If I want std.string.toStringz to put the result into a
> > temporary stack
> > buffer your solution doesn't help at all. Passing an ouput
> > range,
> > allocator or buffer would all solve this.
> 
> I don't understand, why wouldn't you be able to temporarily set the thread-local allocator to use the stack buffer, and restore it once done?

That's possible but insanely dangerous in case you forget to reset the thread allocator. Also storing stack pointers in global state (even thread-local) is dangerous, for example interaction with fibers could lead to bugs, etc. (What if I set the allocator to a stack allocator and call a function which yields from a Fiber?).

You also loose all possibilities to use 'scope' or a similar mechanism to prevent escaping a stack pointer.

Also a stack buffer is not a complete allocator, but in some
cases like toStringz it works even better than allocators (less
overhead as you know the required buffer size before calling toStringz
and there's only one allocation)

And it is a hack. Of course you can provide a wrapper which does
oldAlloc = threadLocalAllocator;
threadLocalAllocator = stackbuf;
func();
scope(exit)
    threadLocalAllocator = oldAlloc;

But how could anybody think this is good API design? I think I'd rather fork the required Phobos functions instead of using such a wrapper.
September 30, 2014
On 9/30/14, 1:34 AM, Johannes Pfau wrote:
> So you propose RC + global/thread local allocators as the solution for
> all memory related problems as 'memory management is not allocation'.
> And you claim that using output ranges / providing buffers / allocators
> is not an option because it only works in some special cases?

Correct. I assume you meant an irony/sarcasm somewhere :o).

> What if I don't want automated memory _management_? What if I want a
> function to use a stack buffer? Or if I want to free manually?
>
> If I want std.string.toStringz to put the result into a temporary stack
> buffer your solution doesn't help at all. Passing an ouput range,
> allocator or buffer would all solve this.

Correct. The output of toStringz would be either a GC string or an RC string.


Andrei

September 30, 2014
On 9/30/14, 3:41 AM, Peter Alexander wrote:
> On Tuesday, 30 September 2014 at 08:34:26 UTC, Johannes Pfau wrote:
>> What if I don't want automated memory _management_? What if I want a
>> function to use a stack buffer? Or if I want to free manually?
>
> Agreed. This is the common case we need to solve for, but this is memory
> allocation, not management. I'm not sure where manual management fits
> into Andrei's scheme. Andrei, could you give an example of, e.g. how
> toStringz would work with a stack buffer in your proposed scheme?

There would be no possibility to do that. I mean it's not there but it can be added e.g. as a "manual" option of performing memory management. The "manual" overloads for functions would require an output range parameter. Not all functions might support a "manual" option - that'd be rejected statically.

> Another thought: if we use a template parameter, what's the story for
> virtual functions (e.g. Object.toString)? They can't be templated.

Good point. We need to think about that.


Andrei


September 30, 2014
On 9/30/14, 3:47 AM, Vladimir Panteleev wrote:
> On Tuesday, 30 September 2014 at 08:34:26 UTC, Johannes Pfau wrote:
>> What if I don't want automated memory _management_? What if I want a
>> function to use a stack buffer? Or if I want to free manually?
>>
>> If I want std.string.toStringz to put the result into a temporary stack
>> buffer your solution doesn't help at all. Passing an ouput range,
>> allocator or buffer would all solve this.
>
> I don't understand, why wouldn't you be able to temporarily set the
> thread-local allocator to use the stack buffer, and restore it once done?

That's doable, but you don't get to place the string at a _specific_ buffer. -- Andrei
September 30, 2014
On Tuesday, 30 September 2014 at 12:02:10 UTC, Johannes Pfau wrote:
> That's possible but insanely dangerous in case you forget to reset the
> thread allocator. Also storing stack pointers in global state (even
> thread-local) is dangerous, for example interaction with fibers could
> lead to bugs, etc. (What if I set the allocator to a stack allocator
> and call a function which yields from a Fiber?).
>
> You also loose all possibilities to use 'scope' or a similar mechanism
> to prevent escaping a stack pointer.

Yes, I agree. One option would be to have thread-local region allocator that can only be used for "scoped" allocation. That is, only for allocations that are not assigned to globals or can get stuck in fibers and that are returned to the calling function. That way the context can free the region when done and you can get away with little allocation overhead if used prudently.

I also don't agree with the sentiment that allocation/management can be kept fully separate. If you have a region allocator that is refcounted it most certainly is interrelated with a fairly tight coupling.

Also the idea exposed in this thread that release()/retain() is purely arithmetic and can be optimized as such is quite wrong. retain() is conceptually a locking construct on a memory region that prevents reuse. I've made a case for TSX, but one can probably come up with other multi-threaded examples.

These hacks are not making D more attractive to people who find C++ lacking in elegance.

Actually, creating a phobos light with nothrow, nogc, a light runtime and basic building blocks such as intrinsics to build your own RC with compiler support sounds like a more interesting option.

I am really not interested in library provided allocators or RC. If I am not going to use malloc/GC then I want to write my own and have dedicated allocators for the most common objects.

I think it is quite reasonable that people who want to take the difficult road of not using GC at all also have to do some extra work, but provide a clean slate to work from!

September 30, 2014
On Tuesday, 30 September 2014 at 12:32:08 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 30 September 2014 at 12:02:10 UTC, Johannes Pfau wrote:
>> ...
>
> > Also the idea exposed in this thread that release()/retain() is
> purely arithmetic and can be optimized as such is quite wrong. retain() is conceptually a locking construct on a memory region that prevents reuse. I've made a case for TSX, but one can probably come up with other multi-threaded examples.
>

It works when two big ifs come together.

- inside the same scope (e.g. function level)

- when the referece is not shared between threads.

While it is of limited applicability, Objective-C (and eventually Swift) codebases prove it helps in most real life use cases.

--
Paulo