File size of compiled Fortran programs - Windows/Linux

Hi everyone. I’m a real beginner with Fortran and have found this site a fantastic resource. My question is actually one my son asked me and I couldn’t answer, so I wondered if anyone might be able to educate me a bit!

So a thing I have found useful in learning the language is doing some simple programs to help my son with his school maths homework - really simple routines to help him check his answers in homework, that kind of thing. I showed him a simple program I did which I ran on both my Linux and Windows laptops. I had originally compiled the code on the Linux (Ubuntu) machine and it only takes up 18Kb on the machine. However, when I compile the very same code on the Windows machine, the file is over 2.5Mb!

There is absolutely no difference in the code and they were both compiled with the respective Windows and Linux versions of gfortran. So he asked me why one file was so much bigger than the other, and I can’t answer him. Any ideas?

3 Likes

@DavidMcP ,

Welcome to the forum, hope both your son and you continue to benefit from Fortran while learning many computing paradigms and platforms!

As to your question, a good one that some GCC/gfortran FOSS volunteer might be able to answer.

By the way, consider what is arguably the shortest conformant Fortran program:

end

How big is the executable on Linux? On Windows, it’s over 2.2 MB!

2 Likes

@FortranFan thanks for your reply! It will definitely be interesting to hear what people think.

I get that end executable to 16.0Kb on Linux :smiley:

I’m sure there is a perfectly good explanation (I did think probably something to do with Windows not being very economical, but that’s a pure guess so I told my son I’d get the real reason from someone who knew a bit about it).

I’m using GNU Fortran (Ubuntu 11.2.0-7ubuntu2) 11.2.0, and Ubuntu 21.10 on the Linux PC and the Windows is Windows 10, but I’ll have to check the gfortran version tomorrow as it’s my work computer. I’m thinking maybe it’s an older version of gfortran, but that hardly would account for such a massive difference.

I haven’t had a chance to investigate this hunch (and I’m not entirely sure to go about doing so), but I suspect that some run-time/system library is being statically linked (i.e. the compiled code for interacting with the system/running any Fortran program is included in the executable file) by default on Windows, but not on Linux. Just a hunch, and I could be totally wrong.

1 Like

There are several different distributions of Gfortran for Windows (Cygwin, Mingw64, etc.) and the following applies only to the Cygwin version 9.3 on Windows 10 X64 that I have.

S:\lang>cat > end.f90
end

S:\lang>gfortran -O2 end.f90 -s

S:\lang>dir a.exe
 Volume in drive S is RAMDISK
 Volume Serial Number is xxxx-yyyy

 Directory of S:\lang

11/29/2021  05:03 PM             8,704 a.exe
               1 File(s)          8,704 bytes
               0 Dir(s)      64,946,176 bytes free
1 Like

Thanks, I seem to remember the Windows distribution I used was one downloaded from the equation.com version shown here

https://fortran-lang.org/learn/os_setup/install_gfortran

which is 10.2.

Not directly an answer, but compilers can optimize for the size of executables. For gfortran:

-Os

Optimize for size. -Os enables all -O2 optimizations except those that often increase code size:

-falign-functions  -falign-jumps 
-falign-labels  -falign-loops 
-fprefetch-loop-arrays  -freorder-blocks-algorithm=stc

It also enables -finline-functions, causes the compiler to tune for code size rather than execution speed, and performs further optimizations designed to reduce code size.


Concerning the Windows executable being larger, I think the right answer is the one from @everythingfunctional. The libgfortran runtime library is probably linked statically on Windows (see Link Options (The GNU Fortran Compiler)). I’ve done some searching but I couldn’t find the right commands needed to see all the steps taken when you call gfortran.

This question from the NAG compiler FAQ’s goes in a similar direction: Is it possible to redistribute applications built with the NAG Fortran Compiler?

1 Like

As a counter example, I compiled and stripped the end program using gfortran 11.2.0 on windows/cygwin. This gives a 9kb executable.

In general the executable size is much larger if it contains debugging information and/or it is statically linked.

$ gfortran -o end.exe end.f90
$ ls -l end.exe
-rwxrwxr-x+ 1 David 161189 Nov 30 09:52 end.exe
$ strip end.exe
$ ls -l end.exe
-rwxrwxr-x+ 1 David  9216 Nov 30 09:52 end.exe

Edit: I see that mecej4 beat me with this.

1 Like

The END program compiled with ifort on Windows is 27K. Most of this is data from the C run-time library.

3 Likes

On many systems you can choose to build a program as a dynamically loaded executable or
as a static or “self-contained” program.

On GNU-Linux the command “ldd” will tell you whether your program is statically linked, or will list all the additional libraries required to execute it that will be loaded when you invoke it.

As mentioned, compiler options can change the size of the executable extensively. The commands that are most useful are “size”,“strip”, and “ldd”. No idea what MSWIndows does; but on ULS (Unix-Like Systems) using gfortran:

$ echo end >end.f90
$ gfortran -static end.f90 -o end.exe
$ ls -ld end.exe
-rwxrwxr-x 1 urbanjs urbanjs **1294296** Nov 29 20:48 end.exe
$ gfortran end.f90 -o end_dl.exe
$ ls -ld end_dl.exe
-rwxrwxr-x 1 urbanjs urbanjs **16624** Nov 29 20:49 end_dl.exe
$ ldd end.exe
        not a dynamic executable
$ ldd end_dl.exe
        linux-vdso.so.1 (0x00007ffdd359e000)
        libgfortran.so.5 => /usr/lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007f6dcf1b4000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6dcefc2000)
        libquadmath.so.0 => /usr/lib/x86_64-linux-gnu/libquadmath.so.0 (0x00007f6dcef78000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6dcee29000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6dcee0e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6dcf4a1000)

In MSWIndows where you might want to build a program you could then execute in any of the different MSWIndows environments it would make sense to have a default be static.

I am a strong proponent of static loading under many circumstances, but if a program uses something like MPI or PC or X11 Windows graphics dynamic loading is almost mandatory so the discussion of the pros and cons can get quite technical, but note that there is a big difference in the size between the two executables above because to actually execute a lot of system libraries for doing everything from loading your program to doing I/O are required. When static, all that gets packed right into your executable so it is in a lot of ways a “stand-alone” executable; in the dynamic executable case the file contains a minimal amount of information mostly about your code and what additional parts it needs to execute; and when you actually execute it in the one case your file is pretty much just loaded into the system and in the other case your program is merged with a lot of other files at the last moment (creating pretty much the equivalent of the static file (well, sort-of) and then executed.

2 Likes

Hi @DavidMcP,

As others have already said, this is because of static vs. dynamic linking. Moreover, I think your observation is specific to the gcc installation that comes from equation.com, which does not ship with any dynamic (dll) versions of the non-windows libraries. As @urbanjost explains, this allows for essentially standalone executables that you can redistribute.

Comparing the minimal END program with both the equation.com gcc build and with the winlibs.com gcc build (from the Quickstart Fortran installer):

$ ls -alh ./*.exe
-rwxr-xr-x 1 UOB+lk12325 UOB+lk12325  54K Nov 30 09:07 ./a-winlibs.exe
-rwxr-xr-x 1 UOB+lk12325 UOB+lk12325 2.2M Nov 30 09:18 ./a-equation.exe

$ ldd a-equation.exe
        ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x7ffab0430000)
        KERNEL32.DLL => /c/Windows/System32/KERNEL32.DLL (0x7ffaafe20000)
        KERNELBASE.dll => /c/Windows/System32/KERNELBASE.dll (0x7ffaae070000)
        msvcrt.dll => /c/Windows/System32/msvcrt.dll (0x7ffab0030000)
$ ldd a-winlibs.exe
        ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x7ffab0430000)
        KERNEL32.DLL => /c/Windows/System32/KERNEL32.DLL (0x7ffaafe20000)
        KERNELBASE.dll => /c/Windows/System32/KERNELBASE.dll (0x7ffaae070000)
        msvcrt.dll => /c/Windows/System32/msvcrt.dll (0x7ffab0030000)
        libgfortran-5.dll => /c/Users/lk12325/AppData/Local/quickstart_fortran/mingw64/bin/libgfortran-5.dll (0x7ffa21cd0000)
        ADVAPI32.dll => /c/Windows/System32/ADVAPI32.dll (0x7ffab0340000)
        sechost.dll => /c/Windows/System32/sechost.dll (0x7ffaae480000)
        RPCRT4.dll => /c/Windows/System32/RPCRT4.dll (0x7ffaae540000)
        libwinpthread-1.dll => /c/Users/lk12325/AppData/Local/quickstart_fortran/mingw64/bin/libwinpthread-1.dll (0x7ffaa7180000)
        libquadmath-0.dll => /c/Users/lk12325/AppData/Local/quickstart_fortran/mingw64/bin/libquadmath-0.dll (0x7ffaa3b80000)
        libgcc_s_seh-1.dll => /c/Users/lk12325/AppData/Local/quickstart_fortran/mingw64/bin/libgcc_s_seh-1.dll (0x7ffa820b0000)
        libgcc_s_seh-1.dll => /c/Users/lk12325/AppData/Local/quickstart_fortran/mingw64/bin/libgcc_s_seh-1.dll (0x29473f30000)

I’m using ldd from MSYS2.
The winlibs.com distribution allows both static and dynamic linking of the non-windows libraries.


PS. It’s great to hear you learning Fortran alongside your son with maths examples. I learnt programming in a similar way by writing small programs for my school maths problems. Maths and programming complement each other really well for learning; maths provides great example problems for programming, and programming teaches a logical and algorithmic mindset for approaching mathematics.

1 Like

Thanks for the replies everyone, really interesting and it looks like I have an answer for my son, as well as a nice bit of learning for myself. I was actually wondering this morning would the use of system calls be the issue. The program I mentioned in my original post uses a simple system call to clear the screen and that’s the only difference in the code between them (because of the use of ‘clear’ in the Ubuntu version, ‘cls’ in Windows), and I noticed that some of the other executables I’ve done from Fortran code in Windows are a little smaller in file size when they don’t use any system calls. But of course, correlation is not causation so that could be pure coincidence.

But now I have read the helpful posts above by @urbanjost , @lkedward and others the static V dynamic linking and the choice of gcc installation makes sense to me.

Once again, very much appreciated, all!

1 Like

No prob. In the past even Unix loaded everything statically, but partly to accomodate machines with smaller memory and file systems dynamic loading became very common. Using dynamic loading allows for the machine to load the same text (executable instructions) being used by several programs in once and let several programs share it, for example.

Now that file space and memory are very much cheaper that feature is less important, but one of the big differences is that if you load something statically it takes a snapshot of the external routines there at the moment and places them in the file; while if something is dynamically loaded it uses the ones available at the time you execute the program; so if the libraries changed you are actually running a different program than the last time you ran .
That might be good. Maybe someone fixed a bug in that library, for example. That might be bad. Maybe someone added a bug to that library, or that library is not on a system where you want to run.

The reason I mentioned the “size” command is because the size of the file can also change depending on how you declared arrays. If they are allocatable they will rarely change the file size significantly; but there are times where what arrays your program declares can change the file size dramatically. Nowadays the compilers are generally quite clever in avoiding creating giant files just because you use giant arrays though.

There are fancier programs like “objdump” for looking at what got put in the file, but those are things you rarely need to look at, but running something like “objdump -x a.out” can be interesting just to look at all the things that actually have to go into a program even as simple as the “end.f90” example just so it can execute.

2 Likes