Thread overview
Why large binary sizes with struct declaration? - why.d
May 14, 2004
Braden MacDonald
May 14, 2004
Kevin Bealer
May 14, 2004
Braden MacDonald
May 14, 2004
Kevin Bealer
May 15, 2004
Braden MacDonald
May 14, 2004
Hi everyone,
 I'm wondering, why does code like this:
____why.d_____________________________________

struct exampleObj {
uint[1024*32] mydata;
}

int main() {
	return 0;
}

____end of
file_______________________________

produce an object file that is 129.6 KB??
As far as I understand, the exampleObj should only be a compile-time
definition, and not an object built into the binary because I never declare an
instance of it. (If I comment out the struct exampleObj declaration, the .o
file goes down to 1.5KB. )

My question is, why does this make a difference?


May 14, 2004
Remember how D allows you to specify initial values for fields in structs? Apparently this is done by using a memcpy from an example object, in new.  This should be more compact and probably faster than the alternative - a procedural constructor definition.  Except in your case, where the object could be built with a loop instead of thousands of assignments.

(I am interpreting gdc assembler output.  I've found it interesting to compare assembler: compile twice with a small change and use a graphical diff util.)

The object you define is not "static" so the definition could be used by another module.  However, I tried making it "static struct {" and that did not have an effect.

Kevin

In article <c81h4p$1uk6$1@digitaldaemon.com>, Braden MacDonald says...
>
>Hi everyone,
> I'm wondering, why does code like this:
>____why.d_____________________________________
> 
>struct exampleObj {
>uint[1024*32] mydata;
>}
> 
>int main() {
>	return 0;
>}
> 
>____end of
>file_______________________________
> 
>produce an object file that is 129.6 KB??
>As far as I understand, the exampleObj should only be a compile-time
>definition, and not an object built into the binary because I never declare an
>instance of it. (If I comment out the struct exampleObj declaration, the .o
>file goes down to 1.5KB. )
> 
>My question is, why does this make a difference?
>
>
>begin 0644 why.d
>M+RH@=VAY+F0*("`@,C(Q+C@@2T(@8V]M<&EL960@)B!L:6YK960*("`@,3(Y
>M+C8@2T(@8V]M<&EL960@*"YO(&9I;&4@;VYL>2D*"B`@("YO8FH@9FEL92!I
>M<R`G;VYL>2<@,2XU2T(@:68@=&AE('-T<G5C="!D969I;FET:6]N(&ES(&-O
>M;6UE;G1E9"!O=70N"BHO"@H*<W1R=6-T(&5X86UP;&5/8FH@>PH)=6EN=%LQ
>M,#(T*C,R72!M>61A=&$["GT*+R\@22!D;VXG="!D96-L87)E(&%N>2!I;G-T
>M86YC97,@;V8@86X@97AA;7!L94]B:B!O8FIE8W0N"@II;G0@;6%I;B@I('L*
>-"7)E='5R;B`P.PI]"GAA
>`
>end


May 14, 2004
Yes, but in my mind, it is only a definition at compile time. The only time I want to use this definition would be from another file, via "import why". ___ How I think of it ______

int;

int main() {
return 0;
}
____________________________

In that code, all I'm saying is that there is such a thing as int, merely for compiler verification of any code referencing it. I am not declaring an int, because I never give it a name. Same thing with the struct. So why does it get into the binary?



The way I use it is something like this:

____helper.d________________
struct Elephant {
uint[1024*32] toes;

int count() {
int count;
for (int i=0;i<toes.length;i++) {
if (toes[i] != 0) {
count++;
}
}
return count;
}
}

______main.d________________
import helper;

int main {
Elephant e1;
ei.toes[7] = 1454325;
printf("The elephant has %d big toes.", e1.count());
return 0;
}
____________________________

So why does the helper.d object file contain binary space for the Elephant object?


Sorry if code is wrong or explanation hard to understand; it's morning here and I'm in a hurry. Thanks.


May 14, 2004
In article <c82mba$i2i$1@digitaldaemon.com>, Braden MacDonald says...
>
>Yes, but in my mind, it is only a definition at compile time. The only time I want to use this definition would be from another file, via "import why".

I think what you are seeing is not an object of this type, but rather static data that is essentially part of the constructor definition and thus created in the module where you define the class.  For an object that is 128 bytes and has a lot of entropy in the initialization values, this technique is probably very beneficial.

Imagine the (generated) constructor definition to look like this:

/+ Note: this is a guess by me, haven't looked at the compiler code. +/

why::exampleObj::this
{
byte * boilerplate = "\0\0\0\0\0....\0"; // 128 KB
memcpy(this, boilerplate, 128*1024*1024);

/+ your constructor would be inlined here +/
}

The same thing happens in the following code:

---

int foo(double x)
{
return x += strlen("alphabet soup");
}

int main(char[][] argv)
{
return argv.size();
}

---

The string literal is never used, and the function is never called; but the binary will probably still contain both.  Because foo() is emitted, the string literal has to be.

Global analysis could possibly remove these unused parts.  It would have to be done at link time, right?  Otherwise another module might call the constructor.

Kevin


May 15, 2004
Ah, I see now. That helps a lot - thanks.

In article
<c82odg$l0i$1@digitaldaemon.com>, Kevin Bealer says...
>I think what you are
seeing is not an object of this type, but rather static
>data that is
essentially part of the constructor definition and thus created in
>the module
where you define the class.  For an object that is 128 bytes and has
>a lot of
entropy in the initialization values, this technique is probably very
>beneficial.
> 
>Imagine the (generated) constructor definition to look like this:
> 
>/+ Note: this is a guess by me, haven't looked at the compiler code. +/
> 
>why::exampleObj::this { byte * boilerplate = "\0\0\0\0\0....\0"; // 128 KB memcpy(this, boilerplate, 128*1024*1024);
> 
>/+ your constructor would be inlined here +/ }