DMD and GDC are unnecessarily using heap allocations for closures
Siarhei Siamashka
siarhei.siamashka at gmail.com
Sun Dec 10 16:50:11 UTC 2023
On Monday, 30 May 2022 at 11:49:44 UTC, max haughton wrote:
> On Monday, 30 May 2022 at 06:47:24 UTC, Siarhei Siamashka wrote:
>> Consider the following example:
>>
>> ```D
>> import std.algorithm, std.range, std.stdio;
>>
>> [...]
>
> Also it's worth noting you can actually make some of these
> range patterns nogc via using zip(repeat(closureVar), blah).map
Yes, this works and
```D
long binary_search(long n, long val, long i) {
return iota(1, i + 1).map!(x => n / x ==
val).assumeSorted.upperBound(false).length;
}
```
can be indeed rewritten as
```D
long binary_search(long n, long val, long i) @nogc {
struct C { long n, val; }
return zip(repeat(C(n, val)), iota(1, i + 1)).map!(x => x[0].n
/ x[1] == x[0].val)
.assumeSorted.upperBound(false).length;
}
```
Results in roughly no performance changes for LDC thanks to
GC2Stack already taking care of it before, a major performance
improvement for GDC (6.467s -> 4.553s) and a major performance
regression for DMD (10.024s -> 13.733s). I'm not concerned about
DMD, because it is known to be bad at optimizing this stuff
anyway. But the readability of the code suffers, even though this
is without any doubt a usable escape hatch for `@nogc` programs.
Thanks a lot for the hint.
Are there no other better solutions/workarounds planned in the
compiler frontend? As a random experiment I tried to plaster the
`scope` keyword everywhere and this unsurprisingly had no effect.
More information about the Digitalmars-d
mailing list