September 10, 2020 Reducing .init effect of a struct that has large static array members | ||||
---|---|---|---|---|
| ||||
[tldr; I have come up with a way of removing all undesired effects of large static array struct members. See conclusion at the bottom.] Continuing the discussion at https://forum.dlang.org/thread/rdk3m2$725$1@digitalmars.com and understanding kinke's comments there better... And this is all with dmd... 1) Assuming that static array members are really required in the program, the following definition is not desirable because S.init is embedded into the binary image as 8000 bytes (I have much bigger ones in the wild): struct S { double[1000] a; } static assert (S.init.sizeof == 8000); // That S.init is embedded into the binary One way of proving that S.init is indeed embedded into the binary is running obj2asm that is shipped with dmd: 'obj2asm deneme.o') So, that is not good. 2) In order to remove that huge S.init from the program, one can initialize the member with '= void': struct S { double[1000] a = void; } pragma(msg, S.init); // <-- Aside: As a side effect, this output is // now just "S()" and does not include // an array of 1000 elements. Although now the binary does not contain an S.init of 8000 bytes, it contains CPU instructions to set 1000 elements: xor EAX,EAX mov 0FFFFE0C0h[RBP],EAX mov 0FFFFE0C4h[RBP],EAX [... many more to set all elements ...] WAT!!! :) That is not good either because now the compiled code is large. (I think and hope other compilers use memset() here.) As explained in that earlier thread and as seen above, contrary to spec (perhaps to an earlier spec?) and fortunately, '= void' does not "leave the elements uninitialized" but the elements are now 0.0. So, that's doubly [pun] bad: The code is large and the elements are not double.nan. 3) To remove that huge struct initialization code, one can @disable the default constructor. And to provide double.nan values, one can provide a function; which may be named specially, or marked with a UDA or some other way. Below, I use a constructor that takes a special type to mark that this is my "default constructor": struct DefaultCtor {} // <-- My special "marker" struct S { double[1000] a = void; @disable this(); this(DefaultCtor) { // <-- My "default constructor" a[] = double.init; // <-- Good: not 0.0 anymore } } void main() { auto s = S(DefaultCtor()); // <-- But now the syntax is ugly import std; assert(s.a[42].isNaN); } 4) CONCLUSION: The following 'defaulted' template makes the syntax acceptable as well at least for Ali: struct DefaultCtor {} // Special type used as a user-defined UDA ;) struct S { double[1000] a = void; // To not embed S.init into the binary @disable this(); // To not generate many CPU instructions this(DefaultCtor) { // My "default constructor". (This could a[] = double.init; // be a template to not even need a // theoretical rvalue parameter.) } } template defaulted(T) { // Generic template for my default constructor syntax enum defaulted = { return T(DefaultCtor()); }(); } void main() { auto s = defaulted!S; // My default construction syntax } That method works for me. Am I missing something? Ali |
Copyright © 1999-2021 by the D Language Foundation