Thread overview
Large .init for class containing void-initialized struct
Dec 07, 2016
Ali Çehreli
Dec 07, 2016
Stefan Koch
Dec 07, 2016
ketmar
Dec 07, 2016
Johan Engelen
Dec 07, 2016
Basile B.
Dec 07, 2016
Ali Çehreli
Dec 07, 2016
Basile B.
Dec 07, 2016
Ali Çehreli
December 06, 2016
tl;dr; go to the TLDR section below. :)

I use the following command line to identify large symbols. Given a binary named 'deneme', the following command line (at least on Linux) produces the 30 largest symbols in the 'deneme' binary that actually take space in application's memory:

  nm --print-size --size-sort --radix=d deneme | tail -30 | grep -v " B "

A test program:

struct S {
    int i;
    double d;
    ubyte[10_000] a;
}

void main() {
}

According to the command line above, the largest symbol in that program is S.init:

[...]
0000000004446100 0000000000003633 T _D4core4time8Duration13_toStringImplMxFNaNbNfZAya
0000000004504980 0000000000003707 T _d_arraysetlengthiT
0000000004511312 0000000000010016 R _D6deneme1S6__initZ

So, the S.init object in that binary is 10016 bytes and that makes sense.

Now, request S.init not be generated by initializing the members with void:

struct S {
    int i = void; // (Actually, this =void is not required)
    double d = void;
    ubyte[10_000] a = void;
}

void main() {
}

Great: Now the large S.init is not a part of the binary: (Well, I think it's still in the BSS section but it does not take space in the memory):

[...]
0000000004446100 0000000000003633 T _D4core4time8Duration13_toStringImplMxFNaNbNfZAya
0000000004504980 0000000000003707 T _d_arraysetlengthiT

The largest symbol is now something else: _d_arraysetlengthiT. Here comes the trouble...

TLDR:

Use the void-initialized struct as a class member and that class gets a huge C.init:

struct S {
    int i = void;
    double d = void;
    ubyte[10_000] a = void;
}

class C {
    S s = void; // (Same result even without the =void)
}

void main() {
}

[...]
0000000004446260 0000000000003633 T _D4core4time8Duration13_toStringImplMxFNaNbNfZAya
0000000004505140 0000000000003707 T _d_arraysetlengthiT
0000000006681456 0000000000010032 V _D6deneme1C6__initZ

Now we have a 10032 byte C.init.

Is there a rationale for this or is this an implementation quality issue? Is there a bug already? I could not find one.

Also, I failed to find the "= void" documentation e.g. not on the struct spec page.

Thank you,
Ali
December 07, 2016
On Wednesday, 7 December 2016 at 00:20:11 UTC, Ali Çehreli wrote:
> tl;dr; go to the TLDR section below. :)
>
> I use the following command line to identify large symbols. Given a binary named 'deneme', the following command line (at least on Linux) produces the 30 largest symbols in the 'deneme' binary that actually take space in application's memory:
>
> [...]
I think this is a bug.

Why is the classInit generated at all ?
We don't guarantee  blit construction of classes./////////////
December 07, 2016
On Wednesday, 7 December 2016 at 00:20:11 UTC, Ali Çehreli wrote:
> Is there a rationale for this or is this an implementation quality issue? Is there a bug already? I could not find one.
>
> Also, I failed to find the "= void" documentation e.g. not on the struct spec page.

https://issues.dlang.org/show_bug.cgi?id=11331
https://issues.dlang.org/show_bug.cgi?id=11817

at least.

i.e.: known inefficiency, but nobody feels that it is important enough to get to the top of the list.
December 07, 2016
On Wednesday, 7 December 2016 at 00:20:11 UTC, Ali Çehreli wrote:
> tl;dr; go to the TLDR section below. :)
>
> [...]
>
> struct S {
>     int i = void;
>     double d = void;
>     ubyte[10_000] a = void;
> }
>
> class C {
>     S s = void; // (Same result even without the =void)
> }
>
> void main() {
> }
>
> [...]
> 0000000004446260 0000000000003633 T _D4core4time8Duration13_toStringImplMxFNaNbNfZAya
> 0000000004505140 0000000000003707 T _d_arraysetlengthiT
> 0000000006681456 0000000000010032 V _D6deneme1C6__initZ
>
> Now we have a 10032 byte C.init.
>
> Is there a rationale for this or is this an implementation quality issue? Is there a bug already? I could not find one.
>
> Also, I failed to find the "= void" documentation e.g. not on the struct spec page.
>
> Thank you,
> Ali

Non initialized classes just don't work. Because of the hidden classes fields an initializer is **always** needed. What happens in your example is that the initializer size is sub optimal.

A naive make without emplace():

====
import std.traits, std.c.stdlib;

CT make(CT, A...)(A a)
{
    auto memory = malloc(__traits(classInstanceSize, CT));
    version(none) emplace!Foo(memory[0..__traits(classInstanceSize, CT)]);
    static if (__traits(hasMember, CT, "__ctor"))
        (cast(CT) (memory)).__ctor(a);
    return cast(CT) memory;
}

class Foo{void foo(){}}

void main()
{
    Foo foo = make!Foo;
    foo.foo;
}
====

crashes with a segfault....
December 07, 2016
On 12/06/2016 06:10 PM, Basile B. wrote:
> On Wednesday, 7 December 2016 at 00:20:11 UTC, Ali Çehreli wrote:
>> tl;dr; go to the TLDR section below. :)
>>
>> [...]
>>
>> struct S {
>>     int i = void;
>>     double d = void;
>>     ubyte[10_000] a = void;
>> }
>>
>> class C {
>>     S s = void; // (Same result even without the =void)
>> }
>>
>> void main() {
>> }
>>
>> [...]
>> 0000000004446260 0000000000003633 T
>> _D4core4time8Duration13_toStringImplMxFNaNbNfZAya
>> 0000000004505140 0000000000003707 T _d_arraysetlengthiT
>> 0000000006681456 0000000000010032 V _D6deneme1C6__initZ
>>
>> Now we have a 10032 byte C.init.
>>
>> Is there a rationale for this or is this an implementation quality
>> issue? Is there a bug already? I could not find one.
>>
>> Also, I failed to find the "= void" documentation e.g. not on the
>> struct spec page.
>>
>> Thank you,
>> Ali
>
> Non initialized classes just don't work. Because of the hidden classes
> fields an initializer is **always** needed. What happens in your example
> is that the initializer size is sub optimal.

Understood. Please confirm whether the following is a bug. Just because a class uses a *pointer* to a void-initialized struct, the struct gets a .init:

struct MyStruct(T) {
    T[10_000] a = void;
}

// Same with struct
class Outer {
    MyStruct!ubyte* s;
}

void main() {
}

0000000006681728 0000000000010000 V _D6deneme15__T8MyStructThZ8MyStruct6__initZ

Make the struct a non-template and MyStruct.init disappears as expected.

Ali

December 07, 2016
On Wednesday, 7 December 2016 at 08:46:13 UTC, Ali Çehreli wrote:
> On 12/06/2016 06:10 PM, Basile B. wrote:
> > On Wednesday, 7 December 2016 at 00:20:11 UTC, Ali Çehreli
> wrote:
> >> tl;dr; go to the TLDR section below. :)
> >>
> >> [...]
> >>
> >> struct S {
> >>     int i = void;
> >>     double d = void;
> >>     ubyte[10_000] a = void;
> >> }
> >>
> >> class C {
> >>     S s = void; // (Same result even without the =void)
> >> }
> >>
> >> void main() {
> >> }
> >>
> >> [...]
> >> 0000000004446260 0000000000003633 T
> >> _D4core4time8Duration13_toStringImplMxFNaNbNfZAya
> >> 0000000004505140 0000000000003707 T _d_arraysetlengthiT
> >> 0000000006681456 0000000000010032 V _D6deneme1C6__initZ
> >>
> >> Now we have a 10032 byte C.init.
> >>
> >> Is there a rationale for this or is this an implementation
> quality
> >> issue? Is there a bug already? I could not find one.
> >>
> >> Also, I failed to find the "= void" documentation e.g. not
> on the
> >> struct spec page.
> >>
> >> Thank you,
> >> Ali
> >
> > Non initialized classes just don't work. Because of the
> hidden classes
> > fields an initializer is **always** needed. What happens in
> your example
> > is that the initializer size is sub optimal.

I've said bullshit here. In case of manual init, gaps couldn't be handled easily anyway. We have the aggregate size, we have a pointer to its initializer, that's all.

>
> Understood. Please confirm whether the following is a bug. Just because a class uses a *pointer* to a void-initialized struct, the struct gets a .init:
>
> struct MyStruct(T) {
>     T[10_000] a = void;
> }
>
> // Same with struct
> class Outer {
>     MyStruct!ubyte* s;
> }
>
> void main() {
> }
>
> 0000000006681728 0000000000010000 V _D6deneme15__T8MyStructThZ8MyStruct6__initZ
>
> Make the struct a non-template and MyStruct.init disappears as expected.
>
> Ali

I wouldn't say it's a bug rather an enhancement request. Unfortunately with my expertise level I can't say more. I think that one of the GDC member expressed some interest into making initialization of aggregates better. It was when A.Alexandrescu worked on RCString, I can't find the link anymore, it was about RCString init being slow, in comparison to a cpp equivalent.

It looks like this discussion is highly related.
December 07, 2016
On 12/07/2016 02:56 AM, Basile B. wrote:

> rather an enhancement request

  https://issues.dlang.org/show_bug.cgi?id=16956

Thank you,
Ali

December 07, 2016
On Wednesday, 7 December 2016 at 00:35:21 UTC, ketmar wrote:
> On Wednesday, 7 December 2016 at 00:20:11 UTC, Ali Çehreli wrote:
>> Is there a rationale for this or is this an implementation quality issue? Is there a bug already? I could not find one.
>>
>> Also, I failed to find the "= void" documentation e.g. not on the struct spec page.
>
> https://issues.dlang.org/show_bug.cgi?id=11331
> https://issues.dlang.org/show_bug.cgi?id=11817
>
> at least.

Yep, many bugs about it.

> i.e.: known inefficiency, but nobody feels that it is important enough to get to the top of the list.

I think what's blocking things is:
```
T a;
T b;
assert(a == b);
```
Someone noted that that's a language guarantee, which would have to be relaxed for aggregates with `= void`initialized fields.

-Johan