Do you use D's GC?

Steven Schveighoffer schveiguy at gmail.com
Tue Aug 3 19:19:46 UTC 2021


On 8/3/21 3:04 PM, jfondren wrote:
> On Tuesday, 3 August 2021 at 18:34:55 UTC, Steven Schveighoffer wrote:
>> On 8/3/21 2:21 PM, jfondren wrote:
>>> On Tuesday, 3 August 2021 at 18:18:48 UTC, Steven Schveighoffer wrote:
>>>> It's no different for length setting:
>>>>
>>>> ```d
>>>> buffer = new int[100];
>>>> auto buffer2 = buffer;
>>>> buffer.length = 50;
>>>> buffer.length = 51; // must reallocate
>>>> buffer[$-1] = 10;
>>>> ```
>>>
>>> All of the buffer-stomping "must reallocate" cases are preserved in 
>>> the rewrite that only assigns buffer.length when the new value is 
>>> larger. The cases that were omitted, that were a performance drain, 
>>> were same-length or less-length assignments.
>>>
>>
>> There is not a reallocation on shrinking of length. Or am I 
>> misunderstanding your statement?
> 
> I think you're understanding it but not believing it. Again:
> 
> ```d
> void ensureHasRoom(ref ubyte[] buffer, size_t length) {
>    buffer.length = length; // this is a serious bottleneck
> }
> 
> void ensureHasRoom(ref ubyte[] buffer, size_t length) {
>    if (buffer.length < length) {
>      buffer.length = length; // this is fine actually
>    }
> }
> ```
> 
> The story is that the situation was improved by excluding only those 
> cases that should not have incurred any reallocations. Every single 
> invocation of the bottlenecking `ensureHasRoom` that should have 
> reallocated would still do so with the fixed `ensureHasRoom`. So 
> something else was going on.

The allocation doesn't happen on the shrinking, it happens on the regrowing.

So e.g. you have:

```d
int[] buf;
ensureHasRoom(buf, 10); // allocates
buf[0 .. 10] = 42;
ensureHasRoom(buf, 5); // does not reallocate in both versions
buf[0 .. 5] = 43;
ensureHasRoom(buf, 10); // Maybe reallocates (see below)
buf[0 .. 10] = 44;
```

So before Ali made the change, the line to regrow to 10 elements DID 
reallocate, because now it might stomp on data held elsewhere (the 
runtime has no way of knowing how many references to the array buffer 
there are, it just knows how much has been used).

After the change, the shrinking to 5 did not actually set the length to 
5, the function left it at 10, so the "regrowing" sets the length from 
10 to 10. This means no reallocation on the *subsequent* length change.

The serious bottleneck is on the growing (unnecessarily), not in the 
shrinking. Clearly, the code in question can handle a buffer that isn't 
the exact length, but has at least enough length. Here's how I would 
write the function (if the buffer had to be the exact length needed):

```d
void ensureHasRoom(ref ubyte[] buffer, size_t length) {
   bool shouldAssume = buffer.length > length;
   buffer.length = length;
   if(shouldAssume) buffer.assumeSafeAppend;
}
```

which would have the same effect (though be slightly less efficient 
since it would still have to be an opaque call into the runtime, and 
possibly have to fetch block metadata).

-Steve


More information about the Digitalmars-d mailing list