Reducing .init effect of a struct that has large static array members
Ali Çehreli
acehreli at yahoo.com
Thu Sep 10 17:43:38 UTC 2020
[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
More information about the Digitalmars-d-learn
mailing list