www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Binary Size: function-sections, data-sections, etc.

reply "dsimcha" <dsimcha yahoo.com> writes:
I started poking around and examining the details of how the GNU 
linker works, to solve some annoying issues with LDC.  In the 
process I the following things that may be useful low-hanging 
fruit for reducing binary size:

1.  If you have an ar library of object files, by default no dead 
code elimination is apparently done within an object file, or at 
least not nearly as much as one would expect.  Each object file 
in the ar library either gets pulled in or doesn't.

2.  When something is compiled with -lib, DMD writes libraries 
with one object file **per function**, to get around this.  GDC 
and LDC don't.  However, if you compile the object files and then 
manually make an archive with the ar command (which is common in 
a lot of build processes, such as gtkD's), this doesn't apply.

3.  The defaults can be overridden if you compile your code with 
-ffunction-sections and -fdata-sections (DMD doesn't support 
this, GDC and LDC do) and link with --gc-sections.  
-ffunction-sections and -fdata-sections cause each function or 
piece of static data to be written as its own section in the 
object file, instead of having one giant section that's either 
pulled in or not.  --gc-sections garbage collects unused 
sections, resulting in much smaller binaries especially when the 
sections are fine-grained.

On one project I'm working on, I compiled all the libs I use with 
GDC using -ffunction-sections -fdata-sections.  The stripped 
binary is 5.6 MB when I link the app without --gc-sections, or 
3.5 MB with --gc-sections.  Quite a difference.  The difference 
would be even larger if Phobos were compiled w/ 
-ffunction-sections and -fdata-sections.  (See 
https://bitbucket.org/goshawk/gdc/issue/293/ffunction-section
-fdata-sections-for 
).

DMD can't compile libraries with -ffunction-sections or 
-fdata-sections and due to other details of my build process that 
are too complicated to explain here, the results from DMD aren't 
directly comparable to those from GDC.  However, --gc-sections 
reduces the DMD binaries from 11 MB to 9 MB.

Bottom line:  If we want to reduce D's binary size there are two 
pieces of low-hanging fruit:

1.  Make -L--gc-sections the default in dmd.conf on Linux and 
probably other Posix OS's.

2.  Add -ffunction-sections and -fdata-sections or equivalents to 
DMD and compile Phobos with these enabled.  I have no idea how 
hard this would be, but I imagine it would be easy for someone 
who's already familiar with object file formats.
Dec 20 2011
next sibling parent reply Trass3r <un known.com> writes:
 Bottom line:  If we want to reduce D's binary size there are two pieces  
 of low-hanging fruit:

 1.  Make -L--gc-sections the default in dmd.conf on Linux and probably  
 other Posix OS's.

 2.  Add -ffunction-sections and -fdata-sections or equivalents to DMD  
 and compile Phobos with these enabled.  I have no idea how hard this  
 would be, but I imagine it would be easy for someone who's already  
 familiar with object file formats.
Seems like --gc-sections _can_ have its pitfalls: http://blog.flameeyes.eu/2009/11/21/garbage-collecting-sections-is-not-for-production Also I read somewhere that --gc-sections isn't always supported (no standard switch or something like that). I personally see no reason not to use -ffunction-sections and -fdata-sections for compiling phobos though, cause a test with gdc didn't even result in a much bigger lib file, nor did it take significantly longer to compile/link. That site I linked claims though, that it does mean serious overhead even if --gc-sections is omitted then. So we have to do tests with huge codebases first.
Dec 20 2011
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 12/20/11 19:59, Trass3r wrote:
 Seems like --gc-sections _can_ have its pitfalls:
 http://blog.flameeyes.eu/2009/11/21/garbage-collecting-sections-is-not-for-production
 
 Also I read somewhere that --gc-sections isn't always supported (no standard
switch or something like that).
The scenario in that link apparently involves a hack, where a completely unused symbol is used to communicate with another program/library (which checks for its presence with dlsym(3)). The linker will omit that symbol, as nothing else references it - the solution is to simply reference it from somewhere. Or explicitly place it in a used section. Or incrementally link in the unused symbols _after_ the gc pass. Or... If you use such hacks you have to handle them specially; there's no way for the compiler to magically know which unreferenced symbols are not really unused. (which is also why this optimization isn't very useful for shared libs - every visible symbol has to be assumed used, for obvious reasons) The one potential problematic case i mentioned in that gdc bug mentioned above is this: If the D runtime (most likely GC) needs to know the start/end of the data and bss sections _and_ does it in a way that can confuse it if some unreferenced parts of these sections disappear and/or are reordered, then turning on the section GC could uncover this bug. From the few simple tests i ran here everything seems to work fine, but I did not check the code to confirm there are no incorrect assumptions present.
 I personally see no reason not to use -ffunction-sections and -fdata-sections
for compiling phobos though, cause a test with gdc didn't even result in a much
bigger lib file, nor did it take significantly longer to compile/link.
737k -> 320k executable size reduction is a compelling argument.
 That site I linked claims though, that it does mean serious overhead even if
--gc-sections is omitted then.
?
 So we have to do tests with huge codebases first.
yes. artur
Dec 20 2011
next sibling parent reply "Kagamin" <spam here.lot> writes:
 The one potential problematic case i mentioned in that gdc bug 
 mentioned above is this:
 If the D runtime (most likely GC) needs to know the start/end 
 of the data and bss
 sections _and_ does it in a way that can confuse it if some 
 unreferenced parts of these
 sections disappear and/or are reordered, then turning on the 
 section GC could uncover
 this bug. From the few simple tests i ran here everything seems 
 to work fine, but I did
 not check the code to confirm there are no incorrect 
 assumptions present.
The first things to break are module ctors and unittests.
Dec 21 2011
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 12/21/11 10:01, Kagamin wrote:
 The one potential problematic case i mentioned in that gdc bug mentioned above
is this:
 If the D runtime (most likely GC) needs to know the start/end of the data and
bss
 sections _and_ does it in a way that can confuse it if some unreferenced parts
of these
 sections disappear and/or are reordered, then turning on the section GC could
uncover
 this bug. From the few simple tests i ran here everything seems to work fine,
but I did
 not check the code to confirm there are no incorrect assumptions present.
The first things to break are module ctors and unittests.
Could you elaborate? Real code, that i was testing with previously, contained static module ctors which worked, and i just tried a simple two module test w/ multiple shared_[cd]tors + static_[cd]tors + unittests -- all of them are called as expected. FWIW for this trivial test module which imports only stdio, --gc-sections alone reduces the stripped executable size from 724416 to 249120. artur
Dec 21 2011
parent reply "Kagamin" <spam here.lot> writes:
 Could you elaborate?
As I remember, these features rely heavily on linker support, namely on how it merges sections. Each pointer to static ctor (and unittest) is placed in a section, then these sections are merged by linker and you get an array of pointers to all static ctors and unittests. The startup code reads the pointers in sequence and executes them. If some section is removed, the corresponding static ctor won't be run.
 Real code, that i was testing with previously, contained static 
 module ctors which
 worked, and i just tried a simple two module test w/ multiple 
 shared_[cd]tors +
 static_[cd]tors + unittests -- all of them are called as 
 expected.
If so then ok.
Dec 21 2011
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 12/21/11 17:42, Kagamin wrote:
 Could you elaborate?
As I remember, these features rely heavily on linker support, namely on how it merges sections. Each pointer to static ctor (and unittest) is placed in a section, then these sections are merged by linker and you get an array of pointers to all static ctors and unittests. The startup code reads the pointers in sequence and executes them. If some section is removed, the corresponding static ctor won't be run.
 Real code, that i was testing with previously, contained static module ctors
which
 worked, and i just tried a simple two module test w/ multiple shared_[cd]tors +
 static_[cd]tors + unittests -- all of them are called as expected.
If so then ok.
I suspected that was what you were worried about and did that test [1], which gives the expected output. But looking now at the list of linker gc'ed sections, i notice ld does claim to have removed eg ".text._D11gcfuncdata218_sharedStaticCtor3FZv", which is one of the ctors. The reason things still work is because the code is also inlined in "_D11gcfuncdata215__modsharedctorFZv" together with the other ctor. Aha, the compiler (gdc) actually inlined all the [cd]tors when run at -O3, and the "real" constructor was then garbage collected by the linker. Phew. Compiling with -O2 makes modsharedctor call all the ctors instead, and they are then not gc'ed. Why the compiler emits the unused outofline ctors is another question. Maybe i should start marking them as private?... Turned out not to be a good idea, as the compiler silently accepts "shared private static this() {...}" and does not emit the dead code anymore. But.. this constructor is run together with the un-shared ones, ie possibly _after_ them... (this is not section-gc specific, happens w/o it too) artur [1] import std.stdio; /* gcfuncdata2.d is a copy of this module, w/o main() */ import gcfuncdata2; auto p(S...)(S args) { return stdout.writeln(__FILE__~": ", args); } void main(string[] args){ p("main"); } static this() { p("ctor1"); } static this() { p("ctor2"); } shared static this() { p("shared ctor1"); } shared static this() { p("shared ctor2"); } static ~this() { p("dtor1"); } static ~this() { p("dtor2"); } shared static ~this() { p("shared dtor1"); } shared static ~this() { p("shared dtor2"); } unittest { p("unittest1"); } unittest { p("unittest2"); }
Dec 21 2011
next sibling parent reply "Kagamin" <spam here.lot> writes:
 Turned out not to be a good idea, as the compiler silently 
 accepts
 "shared private static this() {...}" and does not emit the dead 
 code anymore.
 But.. this constructor is run together with the un-shared ones, 
 ie possibly
 _after_ them... (this is not section-gc specific, happens w/o 
 it too)
try `private shared static this()` AFAIK static ctors have some attribute ordering issue.
 import std.stdio;

 /* gcfuncdata2.d is a copy of this module, w/o main() */
 import gcfuncdata2;

 auto p(S...)(S args) { return stdout.writeln(__FILE__~": ", 
 args); }

 void main(string[] args){ p("main"); }

 static this() { p("ctor1"); }
 static this() { p("ctor2"); }
 shared static this() { p("shared ctor1"); }
 shared static this() { p("shared ctor2"); }

 static ~this() { p("dtor1"); }
 static ~this() { p("dtor2"); }
 shared static ~this() { p("shared dtor1"); }
 shared static ~this() { p("shared dtor2"); }

 unittest { p("unittest1"); }
 unittest { p("unittest2"); }
Also add class static ctors and function static variables.
Dec 21 2011
parent Artur Skawina <art.08.09 gmail.com> writes:
On 12/21/11 20:13, Kagamin wrote:
 Turned out not to be a good idea, as the compiler silently accepts
 "shared private static this() {...}" and does not emit the dead code anymore.
 But.. this constructor is run together with the un-shared ones, ie possibly
 _after_ them... (this is not section-gc specific, happens w/o it too)
try `private shared static this()` AFAIK static ctors have some attribute ordering issue.
Was going to follow up with exactly this. "private shared static this() { p("shared ctor2"); }" is a shared constructor "shared private static this() { p("shared ctor2"); }" is not, but the compiler won't even warn you about this. (in this case gdc, but i assume it's not gdc specific)
 import std.stdio;

 /* gcfuncdata2.d is a copy of this module, w/o main() */
 import gcfuncdata2;

 auto p(S...)(S args) { return stdout.writeln(__FILE__~": ", args); }

 void main(string[] args){ p("main"); }

 static this() { p("ctor1"); }
 static this() { p("ctor2"); }
 shared static this() { p("shared ctor1"); }
 shared static this() { p("shared ctor2"); }

 static ~this() { p("dtor1"); }
 static ~this() { p("dtor2"); }
 shared static ~this() { p("shared dtor1"); }
 shared static ~this() { p("shared dtor2"); }

 unittest { p("unittest1"); }
 unittest { p("unittest2"); }
Also add class static ctors and function static variables.
But what would be the point? They should be GCed if unused and emitted, and wont be if something references them. artur
Dec 21 2011
prev sibling parent "Trass3r" <un known.com> writes:
On Wednesday, 21 December 2011 at 19:04:21 UTC, Artur Skawina 
wrote:
 gc'ed sections, i notice ld does claim to have removed eg
 ".text._D11gcfuncdata218_sharedStaticCtor3FZv", which is one of 
 the ctors.
 The reason things still work is because the code is also 
 inlined in "_D11gcfuncdata215__modsharedctorFZv" together with 
 the other ctor. Aha, the compiler (gdc) actually inlined all 
 the [cd]tors when run at -O3,
 and the "real" constructor was then garbage collected by the 
 linker. Phew.
 Compiling with -O2 makes modsharedctor call all the ctors 
 instead, and they are then not gc'ed.

 Why the compiler emits the unused outofline ctors is another 
 question.
 Maybe i should start marking them as private?...
Maybe they should be marked by the compiler as private by default?
Dec 21 2011
prev sibling parent reply "dsimcha" <dsimcha yahoo.com> writes:
Indeed, a couple small programs I wrote today behave erratically 
w/ gc-sections.  This only seems to occur on DMD, but I'm not 
sure if this is a bug in DMD or if differences in library build 
configurations between compilers (these are workarounds for bugs 
in GDC and LDC) explain it.

On Wednesday, 21 December 2011 at 04:15:21 UTC, Artur Skawina 
wrote:
 On 12/20/11 19:59, Trass3r wrote:
 Seems like --gc-sections _can_ have its pitfalls:
 http://blog.flameeyes.eu/2009/11/21/garbage-collecting-sections-is-not-for-production
 
 Also I read somewhere that --gc-sections isn't always 
 supported (no standard switch or something like that).
The scenario in that link apparently involves a hack, where a completely unused symbol is used to communicate with another program/library (which checks for its presence with dlsym(3)). The linker will omit that symbol, as nothing else references it - the solution is to simply reference it from somewhere. Or explicitly place it in a used section. Or incrementally link in the unused symbols _after_ the gc pass. Or... If you use such hacks you have to handle them specially; there's no way for the compiler to magically know which unreferenced symbols are not really unused. (which is also why this optimization isn't very useful for shared libs - every visible symbol has to be assumed used, for obvious reasons) The one potential problematic case i mentioned in that gdc bug mentioned above is this: If the D runtime (most likely GC) needs to know the start/end of the data and bss sections _and_ does it in a way that can confuse it if some unreferenced parts of these sections disappear and/or are reordered, then turning on the section GC could uncover this bug. From the few simple tests i ran here everything seems to work fine, but I did not check the code to confirm there are no incorrect assumptions present.
 I personally see no reason not to use -ffunction-sections and 
 -fdata-sections for compiling phobos though, cause a test with 
 gdc didn't even result in a much bigger lib file, nor did it 
 take significantly longer to compile/link.
737k -> 320k executable size reduction is a compelling argument.
 That site I linked claims though, that it does mean serious 
 overhead even if --gc-sections is omitted then.
?
 So we have to do tests with huge codebases first.
yes. artur
Dec 21 2011
next sibling parent Sean Kelly <sean invisibleduck.org> writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3D879

On Dec 21, 2011, at 2:24 PM, dsimcha wrote:

 Indeed, a couple small programs I wrote today behave erratically w/ =
gc-sections. This only seems to occur on DMD, but I'm not sure if this = is a bug in DMD or if differences in library build configurations = between compilers (these are workarounds for bugs in GDC and LDC) = explain it.
=20
 On Wednesday, 21 December 2011 at 04:15:21 UTC, Artur Skawina wrote:
 On 12/20/11 19:59, Trass3r wrote:
 Seems like --gc-sections _can_ have its pitfalls:
 =
http://blog.flameeyes.eu/2009/11/21/garbage-collecting-sections-is-not-for= -production
 Also I read somewhere that --gc-sections isn't always supported (no =
standard switch or something like that).
=20
 The scenario in that link apparently involves a hack, where a =
completely unused symbol
 is used to communicate with another program/library (which checks for =
its presence with
 dlsym(3)).
 The linker will omit that symbol, as nothing else references it - the =
solution is to
 simply reference it from somewhere. Or explicitly place it in a used =
section. Or
 incrementally link in the unused symbols _after_ the gc pass. Or...
=20
 If you use such hacks you have to handle them specially; there's no =
way for the compiler
 to magically know which unreferenced symbols are not really unused. =
(which is also why
 this optimization isn't very useful for shared libs - every visible =
symbol has to be
 assumed used, for obvious reasons)
=20
 The one potential problematic case i mentioned in that gdc bug =
mentioned above is this:
 If the D runtime (most likely GC) needs to know the start/end of the =
data and bss
 sections _and_ does it in a way that can confuse it if some =
unreferenced parts of these
 sections disappear and/or are reordered, then turning on the section =
GC could uncover
 this bug. =46rom the few simple tests i ran here everything seems to =
work fine, but I did
 not check the code to confirm there are no incorrect assumptions =
present.
=20
 I personally see no reason not to use -ffunction-sections and =
-fdata-sections for compiling phobos though, cause a test with gdc = didn't even result in a much bigger lib file, nor did it take = significantly longer to compile/link.
=20
 737k -> 320k executable size reduction is a compelling argument.
=20
 That site I linked claims though, that it does mean serious overhead =
even if --gc-sections is omitted then.
=20
 ?
=20
 So we have to do tests with huge codebases first.
=20 yes. =20 artur
=20 =20
Dec 21 2011
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 12/22/11 00:06, Sean Kelly wrote:
 http://d.puremagic.com/issues/show_bug.cgi?id=879
Thanks for that link. The exception testcase (from one of the duplicates) seems to work here. (at least w/ gdc, as i don't have dmd here to test) ----- import std.stdio; /* gcfuncdata2.d is a copy of this module, w/o main() */ import gcfuncdata2; auto p(S...)(S args) { return stdout.writeln(__FILE__~": ", args); } void main(string[] args){ p("main"); try { throw new Exception("muh"); } catch (Exception e) { p(e); } } static this() { p("module ctor1"); } static this() { p("module ctor2"); } shared static this() { p("shared module ctor1"); } shared static this() { p("shared module ctor2"); } static ~this() { p("module dtor1"); } static ~this() { p("module dtor2"); } shared static ~this() { p("shared module dtor1"); } shared static ~this() { p("shared module dtor2"); } unittest { p("unittest1"); } unittest { p("unittest2"); } class blah { static this() { p("class static ctor1"); } shared static this() { p("class shared static ctor1"); } static ~this() { p("class static dtor1"); } shared static ~this() { p("class shared static dtor1"); } } ----- results in: $ ./gcfuncdata1 gcfuncdata2.d: shared module ctor1 gcfuncdata2.d: shared module ctor2 gcfuncdata2.d: class shared static ctor1 gcfuncdata1.d: shared module ctor1 gcfuncdata1.d: shared module ctor2 gcfuncdata1.d: class shared static ctor1 gcfuncdata2.d: module ctor1 gcfuncdata2.d: module ctor2 gcfuncdata2.d: class static ctor1 gcfuncdata1.d: module ctor1 gcfuncdata1.d: module ctor2 gcfuncdata1.d: class static ctor1 gcfuncdata2.d: unittest1 gcfuncdata2.d: unittest2 gcfuncdata1.d: unittest1 gcfuncdata1.d: unittest2 gcfuncdata1.d: main gcfuncdata1.d: object.Exception gcfuncdata1.d(12): muh gcfuncdata1.d: class static dtor1 gcfuncdata1.d: module dtor2 gcfuncdata1.d: module dtor1 gcfuncdata2.d: class static dtor1 gcfuncdata2.d: module dtor2 gcfuncdata2.d: module dtor1 gcfuncdata1.d: class shared static dtor1 gcfuncdata1.d: shared module dtor2 gcfuncdata1.d: shared module dtor1 gcfuncdata2.d: class shared static dtor1 gcfuncdata2.d: shared module dtor2 gcfuncdata2.d: shared module dtor1
Dec 21 2011
prev sibling next sibling parent "Martin Nowak" <dawg dawgfoto.de> writes:
On Tue, 20 Dec 2011 19:14:03 +0100, dsimcha <dsimcha yahoo.com> wrote:

 I started poking around and examining the details of how the GNU linker  
 works, to solve some annoying issues with LDC.  In the process I the  
 following things that may be useful low-hanging fruit for reducing  
 binary size:

 1.  If you have an ar library of object files, by default no dead code  
 elimination is apparently done within an object file, or at least not  
 nearly as much as one would expect.  Each object file in the ar library  
 either gets pulled in or doesn't.

 2.  When something is compiled with -lib, DMD writes libraries with one  
 object file **per function**, to get around this.  GDC and LDC don't.   
 However, if you compile the object files and then manually make an  
 archive with the ar command (which is common in a lot of build  
 processes, such as gtkD's), this doesn't apply.

 3.  The defaults can be overridden if you compile your code with  
 -ffunction-sections and -fdata-sections (DMD doesn't support this, GDC  
 and LDC do) and link with --gc-sections.  -ffunction-sections and  
 -fdata-sections cause each function or piece of static data to be  
 written as its own section in the object file, instead of having one  
 giant section that's either pulled in or not.  --gc-sections garbage  
 collects unused sections, resulting in much smaller binaries especially  
 when the sections are fine-grained.
Only newer versions of binutils actually support --gc-sections. There also was a bug that it clears the EH sections.
 On one project I'm working on, I compiled all the libs I use with GDC  
 using -ffunction-sections -fdata-sections.  The stripped binary is 5.6  
 MB when I link the app without --gc-sections, or 3.5 MB with  
 --gc-sections.  Quite a difference.  The difference would be even larger  
 if Phobos were compiled w/ -ffunction-sections and -fdata-sections.   
 (See  
 https://bitbucket.org/goshawk/gdc/issue/293/ffunction-section
-fdata-sections-for  
 ).

 DMD can't compile libraries with -ffunction-sections or -fdata-sections  
 and due to other details of my build process that are too complicated to  
 explain here, the results from DMD aren't directly comparable to those  
 from GDC.  However, --gc-sections reduces the DMD binaries from 11 MB to  
 9 MB.

 Bottom line:  If we want to reduce D's binary size there are two pieces  
 of low-hanging fruit:

 1.  Make -L--gc-sections the default in dmd.conf on Linux and probably  
 other Posix OS's.

 2.  Add -ffunction-sections and -fdata-sections or equivalents to DMD  
 and compile Phobos with these enabled.  I have no idea how hard this  
 would be, but I imagine it would be easy for someone who's already  
 familiar with object file formats.
Dec 20 2011
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 12/20/2011 10:14 AM, dsimcha wrote:
 1. Make -L--gc-sections the default in dmd.conf on Linux and probably other
 Posix OS's.
I tried that years ago, and it created executables that always crashed. I seem to recall that it removed some crucial sections :-) Maybe things are better now and it will work.
 2. Add -ffunction-sections and -fdata-sections or equivalents to DMD and
compile
 Phobos with these enabled. I have no idea how hard this would be, but I imagine
 it would be easy for someone who's already familiar with object file formats.
I didn't know about those flags.
Dec 20 2011
prev sibling parent reply "Marco Leise" <Marco.Leise gmx.de> writes:
Am 20.12.2011, 19:14 Uhr, schrieb dsimcha <dsimcha yahoo.com>:

 I started poking around and examining the details of how the GNU linker  
 works, to solve some annoying issues with LDC.  In the process I the  
 following things that may be useful low-hanging fruit for reducing  
 binary size:

 1.  If you have an ar library of object files, by default no dead code  
 elimination is apparently done within an object file, or at least not  
 nearly as much as one would expect.  Each object file in the ar library  
 either gets pulled in or doesn't.

 2.  When something is compiled with -lib, DMD writes libraries with one  
 object file **per function**, to get around this.  GDC and LDC don't.   
 However, if you compile the object files and then manually make an  
 archive with the ar command (which is common in a lot of build  
 processes, such as gtkD's), this doesn't apply.

 3.  The defaults can be overridden if you compile your code with  
 -ffunction-sections and -fdata-sections (DMD doesn't support this, GDC  
 and LDC do) and link with --gc-sections.  -ffunction-sections and  
 -fdata-sections cause each function or piece of static data to be  
 written as its own section in the object file, instead of having one  
 giant section that's either pulled in or not.  --gc-sections garbage  
 collects unused sections, resulting in much smaller binaries especially  
 when the sections are fine-grained.

 On one project I'm working on, I compiled all the libs I use with GDC  
 using -ffunction-sections -fdata-sections.  The stripped binary is 5.6  
 MB when I link the app without --gc-sections, or 3.5 MB with  
 --gc-sections.  Quite a difference.  The difference would be even larger  
 if Phobos were compiled w/ -ffunction-sections and -fdata-sections.   
 (See  
 https://bitbucket.org/goshawk/gdc/issue/293/ffunction-section
-fdata-sections-for  
 ).

 DMD can't compile libraries with -ffunction-sections or -fdata-sections  
 and due to other details of my build process that are too complicated to  
 explain here, the results from DMD aren't directly comparable to those  
 from GDC.  However, --gc-sections reduces the DMD binaries from 11 MB to  
 9 MB.

 Bottom line:  If we want to reduce D's binary size there are two pieces  
 of low-hanging fruit:

 1.  Make -L--gc-sections the default in dmd.conf on Linux and probably  
 other Posix OS's.

 2.  Add -ffunction-sections and -fdata-sections or equivalents to DMD  
 and compile Phobos with these enabled.  I have no idea how hard this  
 would be, but I imagine it would be easy for someone who's already  
 familiar with object file formats.
Nice of you to start some discussion on these flags. I use them myself (and a few others that seem to affect code size) in a 'tiny' target inside the D Makefile I use. Currently it looks like this: dmd <sources,directory,bin> -m32 -O -release -noboundscheck -L--strip-all -L-O1 -L-znodlopen -L-znorelro -L--no-copy-dt-needed-entries -L--relax -L--sort-common -L--gc-sections -L-lrt -L--as-needed strip <bin> -R .comment -R .note.ABI-tag -R .gnu.hash -R .gnu.version -R .jcr -R .got That's not even funny, I know :D
Dec 20 2011
parent reply "Trass3r" <un known.com> writes:
On Tuesday, 20 December 2011 at 19:36:19 UTC, Marco Leise wrote:
 Nice of you to start some discussion on these flags. I use them 
 myself (and a few others that seem to affect code size) in a 
 'tiny' target inside the D Makefile I use.
 Currently it looks like this:

 	dmd <sources,directory,bin> -m32 -O -release -noboundscheck 
 -L--strip-all -L-O1 -L-znodlopen -L-znorelro 
 -L--no-copy-dt-needed-entries -L--relax -L--sort-common 
 -L--gc-sections -L-lrt -L--as-needed
 	strip <bin> -R .comment -R .note.ABI-tag -R .gnu.hash -R 
 .gnu.version -R .jcr -R .got

 That's not even funny, I know :D
How far down do you get in terms of size with this?
Dec 20 2011
parent "Marco Leise" <Marco.Leise gmx.de> writes:
Am 20.12.2011, 20:45 Uhr, schrieb Trass3r <un known.com>:

 On Tuesday, 20 December 2011 at 19:36:19 UTC, Marco Leise wrote:
 Nice of you to start some discussion on these flags. I use them myself  
 (and a few others that seem to affect code size) in a 'tiny' target  
 inside the D Makefile I use.
 Currently it looks like this:

 	dmd <sources,directory,bin> -m32 -O -release -noboundscheck  
 -L--strip-all -L-O1 -L-znodlopen -L-znorelro  
 -L--no-copy-dt-needed-entries -L--relax -L--sort-common -L--gc-sections  
 -L-lrt -L--as-needed
 	strip <bin> -R .comment -R .note.ABI-tag -R .gnu.hash -R .gnu.version  
 -R .jcr -R .got

 That's not even funny, I know :D
How far down do you get in terms of size with this?
I've just installed 2.057 and there were changes that affect code size, so I'll test it once more: Simple "Hello, world!" dmd-2.057 normal release build (64-bit): 604_156 bytes + in 32-bit : 490_033 bytes + strip-all : 250_008 bytes + all the other flags : 147_256 bytes (-102_752 bytes, ~41%) gcc-Version 4.6.1 20110627 (gdc 0.30, using dmd 2.055) 32-bit release, smallest : 577_856 bytes Small GUI application using gtk and cairo dmd-2.057 normal release build (32-bit): 5_832_735 bytes + strip-all : 2_519_124 bytes + all the other flags : 2_243_240 bytes (-275_884 bytes, ~11%) gcc-Version 4.6.1 20110627 (gdc 0.30, using dmd 2.055) 32-bit release, smallest : 3_204_832 bytes
Dec 20 2011