October 09, 2014
On Thursday, 9 October 2014 at 15:32:06 UTC, Andrei Alexandrescu wrote:
> On 10/9/14, 7:09 AM, Dicebot wrote:
>> Yes and this is exactly why I am that concerned about recent memory
>> management policy thread. Don has already stated it in his talks but I
>> will repeat important points:
>>
>> 1) We don't try to avoid GC in any way
>> 2) However it is critical for performance to avoid creating garbage in a
>> form of new GC roots
>> 3) Worst part of Phobos is not GC allocations but specifically lot of
>> temporarily garbage allocations
>
> What would be a few good examples of (3)? Thanks.

Infamous setExtensions (https://github.com/D-Programming-Language/phobos/blob/master/std/path.d#L843) immediately comes to mind. Usage of concatenation operators there allocates a new GC root for a new string.

>> This is a very simple issue that will prevent us from using and
>> contributing to majority of Phobos even when D2 port is finished.
>>
>> Switch to input/output ranges as API fundamentals was supposed to fix
>> it.
>
> Unfortunately it doesn't. RC does. Lazy computation relies on escaping ranges all over the place (i.e. as fields inside structs implementing the lazy computation). If there's no way to track those many tidbits, resources cannot be reclaimed timely.

Are you trying to tell me programs I work with do not exist? :)

Usage of output range is simply a generalization of out array parameter used in both Tango and our code. It is _already_ proved to work for our cases. Usage of input ranges is less important but it fits existing Phobos style better.

We also don't usually reclaim resources. Out application usually work by growing constant amount of buffers to the point where they can handle routine workload and staying there will almost 0 GC activity.

I don't understand statement about storing the ranges. The way I have it in mind ranges are tool for algorithm composition. Once you want to store it as a struct field you force range evaluation via output range and store resulting allocated buffer. In user code.

>> Custom management policies as you propose won't fix it at all
>> because garbage will still be there, simply managed in a different way.
>
> I'm not sure I understand this.

Typical pattern from existing D1 code:

// bad
auto foo(char[] arg)
{
    return arg ~ "aaa";
}

vs

// good
auto foo(char[] arg, ref char[] result)
{
    result.length = arg.length +3; // won't allocate if already has capacity
    result[0 .. arg.length] = arg[];
    result[arg.length .. arg.length + 3] = "aaa"[];
}

It doesn't matter if first snippet allocates GC root or ref-counted root. We need the version that does not allocate new root at all (second snippet).
October 09, 2014
"Dicebot" <public@dicebot.lv> wrote:
> On Thursday, 9 October 2014 at 15:00:02 UTC, ixid wrote:
>> Multiple approaches to how library functions can handle memory.
> 
> As long as it allows us avoid creating new GC roots and keep using GC for all allocations at the same time.

To clarify: calling GC.free does remove the root, correct?
October 09, 2014
On Thursday, 9 October 2014 at 15:59:12 UTC, Andrei Alexandrescu wrote:
> "Dicebot" <public@dicebot.lv> wrote:
>> On Thursday, 9 October 2014 at 15:00:02 UTC, ixid wrote:
>>> Multiple approaches to how library functions can handle memory.
>> 
>> As long as it allows us avoid creating new GC roots and keep using GC for
>> all allocations at the same time.
>
> To clarify: calling GC.free does remove the root, correct?

Not before it creates one. When I mean "avoid creating new GC roots" I mean "no GC activity at all other than extending existing chunks"
October 09, 2014
On 10/9/14, 9:00 AM, Dicebot wrote:
> On Thursday, 9 October 2014 at 15:59:12 UTC, Andrei Alexandrescu wrote:
>> "Dicebot" <public@dicebot.lv> wrote:
>>> On Thursday, 9 October 2014 at 15:00:02 UTC, ixid wrote:
>>>> Multiple approaches to how library functions can handle memory.
>>>
>>> As long as it allows us avoid creating new GC roots and keep using GC
>>> for
>>> all allocations at the same time.
>>
>> To clarify: calling GC.free does remove the root, correct?
>
> Not before it creates one. When I mean "avoid creating new GC roots" I
> mean "no GC activity at all other than extending existing chunks"

That's interesting. So GC.malloc followed by GC.free does actually affect things negatively? Also let's note that extending existing chunks may result in new allocations.

Andrei

October 09, 2014
Am Thu, 09 Oct 2014 15:57:15 +0000
schrieb "Dicebot" <public@dicebot.lv>:

> > Unfortunately it doesn't. RC does. Lazy computation relies on escaping ranges all over the place (i.e. as fields inside structs implementing the lazy computation). If there's no way to track those many tidbits, resources cannot be reclaimed timely.
> 
> Are you trying to tell me programs I work with do not exist? :)
> 
> Usage of output range is simply a generalization of out array parameter used in both Tango and our code. It is _already_ proved to work for our cases. Usage of input ranges is less important but it fits existing Phobos style better.
> 
> We also don't usually reclaim resources. Out application usually work by growing constant amount of buffers to the point where they can handle routine workload and staying there will almost 0 GC activity.
> 
> I don't understand statement about storing the ranges. The way I have it in mind ranges are tool for algorithm composition. Once you want to store it as a struct field you force range evaluation via output range and store resulting allocated buffer. In user code.

I think Andrei means at some point you have to 'store the range' or create an (often dynamic) array from the range and then you still need some sort of memory management.

Ultimately you still need some sort of memory management there and RC will be nice for that if you don't want to use the GC. You can also store the range to a stack buffer or use manual memory management. But ranges alone do not solve this problem and forcing everyone to do manual memory management is not a good replacement for GC, so we need the discussed RC scheme. At least this is how I understood Andrei's point.
October 09, 2014
On 10/9/14, 8:57 AM, Dicebot wrote:
> On Thursday, 9 October 2014 at 15:32:06 UTC, Andrei Alexandrescu wrote:
>> Unfortunately it doesn't. RC does. Lazy computation relies on escaping
>> ranges all over the place (i.e. as fields inside structs implementing
>> the lazy computation). If there's no way to track those many tidbits,
>> resources cannot be reclaimed timely.
>
> Are you trying to tell me programs I work with do not exist? :)

In all likelihood it's a small misunderstanding.

> Usage of output range is simply a generalization of out array parameter
> used in both Tango and our code. It is _already_ proved to work for our
> cases.

Got it. Output ranges work great with unstructured/linear outputs - preallocate an array, fill it with stuff, all's nice save for the occasional reallocation when things don't fit etc.

With structured outputs there are a lot more issues to address: one can think of a JSONObject as an output range with put() but that's only moving the real issues around. How would the JSONObject allocate memory internally, give it out to its own users, and dispose of it timely, all in good safety?

That's why JSON tokenization is relatively easy to do lazily/with output ranges, but full-blown parsing becomes a different proposition.


Andrei

October 09, 2014
On 10/9/14, 9:27 AM, Johannes Pfau wrote:
> Am Thu, 09 Oct 2014 15:57:15 +0000
> schrieb "Dicebot" <public@dicebot.lv>:
>
>>> Unfortunately it doesn't. RC does. Lazy computation relies on
>>> escaping ranges all over the place (i.e. as fields inside
>>> structs implementing the lazy computation). If there's no way
>>> to track those many tidbits, resources cannot be reclaimed
>>> timely.
>>
>> Are you trying to tell me programs I work with do not exist? :)
>>
>> Usage of output range is simply a generalization of out array
>> parameter used in both Tango and our code. It is _already_ proved
>> to work for our cases. Usage of input ranges is less important
>> but it fits existing Phobos style better.
>>
>> We also don't usually reclaim resources. Out application usually
>> work by growing constant amount of buffers to the point where
>> they can handle routine workload and staying there will almost 0
>> GC activity.
>>
>> I don't understand statement about storing the ranges. The way I
>> have it in mind ranges are tool for algorithm composition. Once
>> you want to store it as a struct field you force range evaluation
>> via output range and store resulting allocated buffer. In user
>> code.
>
> I think Andrei means at some point you have to 'store the range' or
> create an (often dynamic) array from the range and then you still need
> some sort of memory management.
>
> Ultimately you still need some sort of memory management there and RC
> will be nice for that if you don't want to use the GC. You can also
> store the range to a stack buffer or use manual memory management. But
> ranges alone do not solve this problem and forcing everyone to do
> manual memory management is not a good replacement for GC, so we need
> the discussed RC scheme. At least this is how I understood Andrei's
> point.

Yes, that's accurate. Thanks! -- Andrei


October 09, 2014
On Thursday, 9 October 2014 at 16:22:52 UTC, Andrei Alexandrescu wrote:
>>> To clarify: calling GC.free does remove the root, correct?
>>
>> Not before it creates one. When I mean "avoid creating new GC roots" I
>> mean "no GC activity at all other than extending existing chunks"
>
> That's interesting. So GC.malloc followed by GC.free does actually affect things negatively?

Yes and quite notably so as GC.malloc can potentially trigger collection. With concurrent GC collection is not a disaster but it still affects the latency and should be avoided.

> Also let's note that extending existing chunks may result in new allocations.

Yes. But as those chunks never get free'd it comes to O(1) allocation count over process lifetime with most allocations happening during program startup / warmup.

Don has mentioned this as one of important points during his DConf 2014 talk but it probably didn't catch as much attention as it should.
October 09, 2014
On Thursday, 9 October 2014 at 16:41:22 UTC, Andrei Alexandrescu wrote:
>> Usage of output range is simply a generalization of out array parameter
>> used in both Tango and our code. It is _already_ proved to work for our
>> cases.
>
> Got it. Output ranges work great with unstructured/linear outputs - preallocate an array, fill it with stuff, all's nice save for the occasional reallocation when things don't fit etc.
>
> With structured outputs there are a lot more issues to address: one can think of a JSONObject as an output range with put() but that's only moving the real issues around. How would the JSONObject allocate memory internally, give it out to its own users, and dispose of it timely, all in good safety?
>
> That's why JSON tokenization is relatively easy to do lazily/with output ranges, but full-blown parsing becomes a different proposition.

This reminds me of our custom binary serialization utilities, intentionally designed in a way that deserialization can happen in-place using same contiguous data buffer as serialized chunk. It essentially stores all indirections in the same buffer one after other.

Implementation is far from being trivial and adds certain usage restrictions but it allows for the same extremely performant linear buffer approach even with non-linear data structures.

In general I am not trying to argue that range-based approach is a silver bullet though. It isn't and stuff like ref counting will be necessary at least in some domains.

What I am arguing is that it won't solve _our_ issues with Phobos (contrary to previous range-based proposal) and this is the reason for being dissapointed. Twice as so because you suggested to close PR that turns setExtension into range because of your new proposal (which implies that efforts can't co-exist)
October 09, 2014
On 09/25/2014 02:49 PM, H. S. Teoh via Digitalmars-d wrote:
>
> Make-heads find the idea of the compiler being part of the input to a
> build rule "strange"; to me, it's common sense.

Yes. This is exactly why (unless it's been reverted or regressed? I only mention that because I haven't looked lately) RDMD counts the compiler itself as a dependency. Because:

$ dvm use 2.065.0
$ rdmd stuff.d
[compiles]
$ dvm use 2.066.0
$ rdmd stuff.d
[silently *doesn't* recompile?!? Why is *that* useful?]

Is *not* remotely useful behavior, basically makes no sense at all, *and* gives the programmer bad information. ("Oh, it compiles fine on this new version? Great! I'm done here! Wait, why are other people reporting compile errors on the new compiler? It worked for me.")