www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Large .init for class containing void-initialized struct

reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Dec 06 2016
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
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./////////////
Dec 06 2016
prev sibling next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
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.
Dec 06 2016
parent Johan Engelen <j j.nl> writes:
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
Dec 07 2016
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
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....
Dec 06 2016
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Dec 07 2016
parent reply Basile B. <b2.temp gmx.com> writes:
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.
Dec 07 2016
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Dec 07 2016