www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - 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
Sep 10 2020