Reducing Executable Size

From WxWiki
Jump to navigation Jump to search

Even though disk space and RAM are practically dirt-cheap nowadays smaller executables lead to better cache usage, and can be faster. Moreover, users may be downloading over slow modems, and for paid hosting, server bandwidth might be minimized.

General tips

  1. Only link libraries you actually use (e.g. if you don't use rich text and HTML, don't include the wxWidgets libraries that contain them). Obviously, making a monolithic build prevents this, so the first step if you want to keep the size down is to make a non-monolithic build if this is not already the case. See http://docs.wxwidgets.org/trunk/page_libs.html for the list of wx libraries
  2. Make a 'release build' instead of a 'debug build':
    • Windows : Use 'BUILD=release' as an argument while executing the makefile in wxWidgets 2.8 and later.
    • UNIX : it's the default; see configure settings for full details.
    • Since wxWidgets 2.9, debug and release builds have been somewhat unified, which means that assertions will still be compiled in release builds of wxWidgets by default. So to fully remove assertions you'll need to make a release build where debugging support is completely removed (--disable-debug) if you don't make use of them in release builds.
  3. Turn on optimizations for your compiler to optimize for size:
  4. Disable components/features you do not use
    • UNIX : while running './configure' (see ./configure --help for details)
      • Sometimes configure options will cause compiler errors because parts of the library aren't properly #ifdef'ed. Usually these are simple to fix, and patches are appreciated.
    • Windows : by editing include/wx/msw/setup.h manually if using MSVC
    • It's been reported that disabling exceptions ('--enable-no_exceptions' with configure) can make quite a difference.
    • The libtiff library occupies between 200-300k of code and data. Consider removing tiff support from your program to save space.
  5. Use Microsoft Visual C++ instead of gcc (Cygwin or Mingw) on Windows. It does produce smaller and faster executables.
  6. Most installers compress the executable for distribution. So before panicking about the download size of your application, check its compressed size!
  7. Use an executable packer like UPX (e.g. with "-9" and "--ultra-brute" flags) or ASPack. Note that packing affects the startup time of an executable because the entire executable has to be paged.
  8. If you link dynamically (e.g., use wxWidgets as a DLL), you'll end up with a smaller executable, but you'll be required to distribute the wxWidgets DLL, too, which can be quite big. This might be a good option when distributing multiple wxWidgets programs as part of a package, but if you are only distributing one application, you will end up with a simpler package if you statically link wxWidgets since the linker will automatically strip out components that your application doesn't use that it doesn't strip when you create a DLL.
  9. Disabling RTTI (--enable-no_rtti in configure) might make a difference

Windows Specific

MSDN documentation page for Microsoft Visual C++ compiler options: http://msdn.microsoft.com/en-us/library/19z1t1wy.aspx

Win32 Release libraries built by Visual Studio use around 1.2mb of space in your executable when compiled in release mode with the default options. By aggressively stripping options from setup.h it is possible to reduce the size of the library to about 400k, and still have a fairly functional library.

If using Visual Studio 2005, the free MSVC .NET 2003 Toolkit, or older professional Visual C++ editions that come with the optimizing compiler, you can set optimizations to "Minimize Size" instead of "Maximize Speed" (see project properties).

Also, if you have a lot of experience with C/C++ and don't mind delving into some black magic, you can eliminate C runtime stubs and strip other libraries. Don't forget extern "C" _fltused; if using floats. You need to make a function for creating large arrays (it's part of the source in c standard library that comes with MSVC).

See also http://www.nopcode.com/AggressiveOptimize.shtml for a header and some other settings you can set manually to reduce executable size by changing linker alignment and padding.

See Trimming wxWidgets Aggressively for some notes and numbers about reducing executable size under Windows for wxWidgets 2.6 & 2.8

GCC Specific

GCC documentation page for optimization options: http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

  1. Strip your binary
    • Pass the -s flag to gcc while building your executable
    • Or use strip --strip-all SOMEBINARY[.exe] on the executable if using GCC (any platform; on OS X, omit the --strip-all flag) - this might work better than -s in case of static libraries since it will also remove symbols linked in.
    • (Linux only?) Passing --remove-section=.comment and --remove-section=.note to strip saved me 0.31% by removing many copies of "GCC: (GNU) 4.0.1 20050727 (Red Hat 4.0.1-5)"
  2. Pass -ffunction-sections -fdata-sections to the compiler while building and -Wl,--gc-sections to the linker (see http://utilitybase.com/article/show/2007/04/09/225/Size+does+matter:+Optimizing+with+size+in+mind+with+GCC)

Examples and results

In the following tests wx2.9 was used, but the general trend holds for all wxWidgets versions and in general for any piece of software!

Test #1

# wxGTK compiled with:
#   ./configure --disable-debug --disable-shared
#   make
$ ls -l minimal
-rwxr-xr-x 1 frm frm 4,3M 2009-02-11 17:04 minimal*
$ size minimal
   text    data     bss     dec     hex filename
3698330   10436   49692 3758458  39597a minimal
$ strip --strip-all minimal
$ ls -l minimal
-rwxr-xr-x 1 frm frm 3,6M 2009-02-11 17:23 minimal*
$ strip --remove-section=.comment --remove-section=.note minimal
$ ls -l minimal
-rwxr-xr-x 1 frm frm 3,6M 2009-02-11 17:28 minimal*

I.e. using --strip-all provided a 17% improvement. Removing the .comment and .note sections didn't produce any noticeable enhancement.

Test #2

Now, let's test the -ffunctions-sections & co options:

# wxGTK compiled with:
#   export CXXFLAGS="-ffunction-sections -fdata-sections"
#   export LDFLAGS="-Wl,--gc-sections"
#   ./configure --disable-debug --disable-shared
#   make
$ ls -l minimal
-rwxr-xr-x 1 frm frm 3,1M 2009-02-11 17:34 minimal*
$ strip --strip-all minimal
$ ls -l minimal
-rwxr-xr-x 1 frm frm 2,6M 2009-02-11 17:36 minimal*

As you can see, using the -ffunctions-sections option when compiling and the -Wl,--gc-sections option when linking produces great results. The final executable (after stripping) is 30% smaller than the one obtained in test #1!

Test #3

Now, let's test the -Os option (together with the -ffunction-sections option):

# wxGTK compiled with:
#   export CXXFLAGS="-ffunction-sections -fdata-sections -Os"
#   export LDFLAGS="-Wl,--gc-sections"
#   ./configure --disable-debug --disable-shared
#   make
$ ls -l minimal
-rwxr-xr-x 1 frm frm 2,5M 2009-02-11 18:35 minimal*
$ strip --strip-all minimal
$ ls -l minimal
-rwxr-xr-x 1 frm frm 1,8M 2009-02-11 18:35 minimal*

The final executable (after stripping) is 50% smaller than the one obtained in test #1!

Conclusions

The tests showed that:

  1. you should always run strip --strip-all on the final executable (assuming that you don't care about symbol informations): this alone usually gives a 20-30% improvement
  2. if possible, compile all libraries that you're going to statically link in your program with the -ffunction-sections -fdata-sections -Os options and remember to link with the -Wl,--gc-sections option; this gives a 30-50% improvement

Please note also that the results of tests #1-#3 were obtained without disabling (at configure-time) any wxWidgets feature. If you play with the --enable-* options of wxWidgets configure you'll get even better results (see the #General tips).