zeroDivisible
zeroDivisible

Reputation: 4130

GCC C++ "Hello World" program -> .exe is 500kb big when compiled on Windows. How can I reduce its size?

I just recently started learning C++ - I am using nuwen's version of MingW on Windows, using NetBeans as an IDE (I have also MSDN AA Version of MSVC 2008, though I don't use it very often).

When compiling this simple program:

#include <iostream>
using namespace std;

int dog, cat, bird, fish;

void f(int pet) {
  cout << "pet id number: " << pet << endl;
}

int main() {
  int i, j, k;
  cout << "f(): " << (long)&f << endl;
  cout << "dog: " << (long)&dog << endl;
  cout << "cat: " << (long)&cat << endl;
  cout << "bird: " << (long)&bird << endl;
  cout << "fish: " << (long)&fish << endl;
  cout << "i: " << (long)&i << endl;
  cout << "j: " << (long)&j << endl;
  cout << "k: " << (long)&k << endl;
} ///:~

my executable was about 1MB big. When I changed project configuration from Debug to Release, used -O1 -Os flags ( stripping debugging symbols along the way ), binary size was reduced from 1MB to 544KB.

I am not a "size freak", but I am just wondering - is there any way, that I could reduce .exe size even more? I just think, that 544KB is just too much for such a simple application ).

Upvotes: 35

Views: 35406

Answers (14)

Pedro Lobito
Pedro Lobito

Reputation: 98921

Very late answer, but maybe helpful for new c++ users that ended here searching for "reducing hello world binary size":

Here's the steps I've done to compile a hello_world.exe with ~20KB:

hello_world.cpp

#include <cstdio>  // not <iostream>
int main()
{
 printf("Hello, World");
 return 0;
}

g++ cmd line

g++ -s hello_world.cpp -o hello_world.exe

UPX to reduce the binary in approx 50%:

upx -9 hello_world.exe

      File size         Ratio      Format      Name
 --------------------   ------   -----------   -----------
   40448 ->  20992      51.90%    win64/pe     hello_world.exe   

Upvotes: 0

Plakhoy
Plakhoy

Reputation: 1872

The problem here is not so much with the library as it is with the way the
library is linked. Granted, iostream is a moderately huge library but I don't
think it can be so huge as to cause a program to generate an executable that is
900KB larger than a similar one that uses C functions. The one to blame
is not iostream but gcc. More accurately, static linking is to be blamed.

How would you explain these results(with your program):

g++ test.cpp -o test.exe              SIZE: 935KB
gcc test.cpp -o test.exe -lstdc++     SIZE: 64.3KB

Different sizes of executables are being generated with exactly the same
build options.

The answer lies in the way gcc links the object files.
When you compare the outputs from these two commands:

g++ -v test.cpp -o test.exe // c++ program using stream functions  
gcc -v test.c -o test.exe   // c program that using printf  

you'll find out that the only places they differ(apart from the paths to the
temporary object files) is in the options used:

   C++(iostream) | C(stdio)
-------------------------------
-Bstatic         |  (Not There)
-lstdc++         |  (Not There)
-Bdynamic        |  (Not There)
-lmingw32        | -lmingw32 
-lgcc            | -lgcc 
-lmoldname       | -lmoldname 
-lmingwex        | -lmingwex 
-lmsvcrt         | -lmsvcrt 
-ladvapi32       | -ladvapi32 
-lshell32        | -lshell32 
-luser32         | -luser32 
-lkernel32       | -lkernel32 
-lmingw32        | -lmingw32 
-lgcc            | -lgcc 
-lmoldname       | -lmoldname 
-lmingwex        | -lmingwex 
-lmsvcrt         | -lmsvcrt 

You've got your culprit right there at the top. -Bstatic is the option that comes
exactly after the object file which may look something like this:

"AppData\\Local\\Temp\\ccMUlPac.o" -Bstatic -lstdc++ -Bdynamic ....

If you play around with the options and remove 'unnecessary' libraries,
you can reduce the size of the executable from 934KB to 4.5KB max
in my case. I got that 4.5KB by using -Bdynamic, the -O flag
and the most crucial libraries that your application can't live without, i.e
-lmingw32, -lmsvcrt, -lkernel32. You'll get a 25KB executable at that
point. Strip it to 10KB and UPX it to around 4.5KB-5.5KB.

Here's a Makefile to play with, for kicks:

## This makefile contains all the options GCC passes to the linker
## when you compile like this: gcc test.cpp -o test.exe
CC=gcc

## NOTE: You can only use OPTIMAL_FLAGS with the -Bdynamic option. You'll get a
## screenfull of errors if you try something like this: make smallest type=static
OPTIMAL_FLAGS=-lmingw32 -lmsvcrt -lkernel32

DEFAULT_FLAGS=$(OPTIMAL_FLAGS) \
-lmingw32 \
-lgcc \
-lmoldname \
-lmingwex \
-lmsvcrt \
-ladvapi32 \
-lshell32 \
-luser32 \
-lkernel32 \
-lmingw32 \
-lgcc  \
-lmoldname \
-lmingwex \
-lmsvcrt


LIBRARY_PATH=\
-LC:\MinGW32\lib\gcc\mingw32\4.7.1 \
-LC:\mingw32\lib\gcc \
-LC:\mingw32\lib\mingw32\lib \
-LC:\mingw32\lib\

OBJECT_FILES=\
C:\MinGW32\lib\crt2.o \
C:\MinGW32\lib\gcc\mingw32\4.7.1\crtbegin.o

COLLECT2=C:\MinGW32\libexec\gcc\mingw32\4.7.1\collect2.exe

normal:
    $(CC) -c test.cpp
    $(COLLECT2) -Bdynamic $(OBJECT_FILES)  test.o -B$(type) -lstdc++ -Bdynamic  $(DEFAULT_FLAGS) $(LIBRARY_PATH) -o test.exe

optimized:
    $(CC) -c -O test.cpp
    $(COLLECT2) -Bdynamic $(OBJECT_FILES)  test.o -B$(type) -lstdc++ -Bdynamic  $(DEFAULT_FLAGS) $(LIBRARY_PATH) -o test.exe

smallest:
    $(CC) -c -O test.cpp
    $(COLLECT2) -Bdynamic $(OBJECT_FILES)  test.o -B$(type) -lstdc++ -Bdynamic  $(OPTIMAL_FLAGS) $(LIBRARY_PATH) -o test.exe

ultimate:
    $(CC) -c -O test.cpp
    $(COLLECT2) -Bdynamic $(OBJECT_FILES)  test.o -B$(type) -lstdc++ -Bdynamic  $(OPTIMAL_FLAGS) $(LIBRARY_PATH) -o test.exe
    strip test.exe
    upx test.exe

CLEAN:
    del *.exe *.o

Results(YMMV):

// Not stripped or compressed in any way
make normal    type=static     SIZE: 934KB
make normal    type=dynamic    SIZE: 64.0KB

make optimized type=dynamic    SIZE: 30.5KB
make optimized type=static     SIZE: 934KB

make smallest  type=static     (Linker Errors due to left out libraries)
make smallest  type=dynamic    SIZE: 25.6KB 

// Stripped and UPXed
make ultimate type=dynamic    (UPXed from 9728 bytes to 5120 bytes - 52.63%)
make ultimate type=static     (Linker Errors due to left out libraries)

A possible reason for the inclusion of -Bstatic in the default build options
is for better performance. I tried building astyle with -Bdynamic and got
a speed decrease of 1 second on average, even though the application was way
smaller than the original(400KB vs 93KB when UPXed).

Upvotes: 60

Antoni Gual Via
Antoni Gual Via

Reputation: 763

If you need small executables, Tiny C will compile a 1536 bytes executable for a printf("Hello world!") TinyC is only C, not C++ and is known to compile faster and give slower executables than gcc.

EDITED: I have just tried a cout<"Hello World!" in DevC++ (bundles Mingw 4.8 and an Ide) and i got a 4,5 MB executable!!

Upvotes: 3

Colin Pickard
Colin Pickard

Reputation: 46653

Not sure how much use it will be to you, but someone has done quite a lot of work on reducing the size of a simple Windows .exe.

They were able to create a simple .exe that will execute on a modern version of Windows in 133 bytes, by using some very extreme methods.

Upvotes: 11

greatwolf
greatwolf

Reputation: 20838

How is it that other compilers like msvc8 or even an order compiler like borland c++ 5.5.1 are capable of producing very small executables but mingw gcc isn't able to?

I did a quick compile of a 'hello world' for each of the following toolsets and observed the compiled executable size. Please note that in all these cases the runtime library is statically linked and all debug symbols have been stripped:

compiler toolchain            exe size                   exe size
                              (w/iostream header)        (w/cstdio printf)
-------------------------------------------------------------------------
Borland C++ 5.5.1             110kbyte                    52kbyte
MSVC 2008 express             102kbyte                    55kbyte
MinGW- GCC 3.4.5              277kbyte                    <10kbyte
MinGW- GCC 4.4.1              468kbyte                    <10kbyte

What's interesting is the later version of gcc 4.4.1 produces an even larger executable than gcc3.4.5, probably due to different version of libstdc++.

So is there really no way to remove dead code during the linking phase for mingw?

Upvotes: 1

Kaz Dragon
Kaz Dragon

Reputation: 6809

I replicated your test using Cygwin and g++. Your code compiled to 480k with -O2. Running strip on the executable reduced it to 280k.

In general, though, I suspect your problem is the use of the <iostream> header. This causes a fairly large library to be linked in. Also, note that cout << x does a lot more than just printing. There are locales and streams and all sorts of under-the-hood stuff.

If however, having a small executable size is a real, mission-critical objective, then avoid it and use printf or puts. If it's not, then I would say pay the one-time cost of iostream and be done with it.

Upvotes: 2

Randai
Randai

Reputation: 111

You could use -s, which I believe is built into mingw as well. A simple hello world application compiled using g++ 3.4.4 on cygwin produced executable that was 476872 bytes, compiling again with -s (strips unnecessary data), reduced the same executable to 276480 bytes.

The same hello world application on cygwin using g++ 4.3.2 produced an executable of 16495 bytes, using strip reduced the size to 4608 bytes. As far as I can see, probably best to use more recent version of g++.

MingW has just released gcc 4.4.0, so if the executable size is important then I'd consider using that. As it indicates -s will probably help strip much of the debugging information out for you, that is only recommended if it is for production use.

Upvotes: 9

Mike Dunlavey
Mike Dunlavey

Reputation: 40669

If you use the "nm" utility or some other program that shows whats in your .exe, you'll see that it contains tons of classes that someone might want to use, but that you don't.

Upvotes: 2

Gerald
Gerald

Reputation: 23499

Basically, there's not really anything you can do to reduce that .exe size with a base distribution of mingw. 550kb is about as small as you can get it, because mingw and gcc/g++ in general are bad at stripping unused functions. About 530kb of that is from the msvcrt.a library.

If you really wanted to get into it, you might be able to rebuild the msvcrt.a library with -ffunction-sections -fdata-sections compiler options, and then use the -Wl,--gc-sections linker options when linking your app, and this should be able to strip a lot of that stuff out of there. But if you're just learning C++, rebuilding that library may be a bit advanced.

Or you could just use MSVC, which is great at stripping unused functions. That same bit of code compiled with MSVC produces a 10kb exe.

Upvotes: 4

Niels Basjes
Niels Basjes

Reputation: 10642

The major part of the size stems from the use of fairly extensive runtime libraries. So in real life you are actually linking a very large piece of 'dead code' if you have such a simple application.

As far as I know there are no linker flags to skip the unused parts of a linked library.

There are two ways I know of to fake a smaller application:

  1. Use dynamic linking. Then your application refers to the dynamically loaded library. You still need the full size (actually more) but you have a much smaller executable.
  2. Use an executable compression system.

Upvotes: 0

anon
anon

Reputation:

The

#include <iostream>

causes a lot of the standard library to be linked in, at least with g++. If you are really concerned about executable size, try replacing all uses of iostreams with printf or similar. This will typically give you a smaller, faster executable (I got yours down to about 6K) at the cost of convenience and type-safety.

Upvotes: 24

SteveL
SteveL

Reputation: 1821

You could always run UPX on your exe after you have created it.

Upvotes: 3

Raoul Supercopter
Raoul Supercopter

Reputation: 5114

Well when you use C++ standard library, exe can get big really quickly. If after stripping debug symbol, you still want to reduce the size of your software, you can use a packer like UPX. But, be warned, some antivirus choke on exe packed with UPX as some virus used it a long time ago.

Upvotes: 2

nos
nos

Reputation: 229108

You get the C++ standard library, and other stuff I guess, statically linked in as mingw has its own implementation of these libraries.

Don't worry so much about it, when you make more complex program, the size won't grow accordingly.

Upvotes: 8

Related Questions