Recent

Author Topic: I built and compiled a Hello, World! and am shocked at the executable size  (Read 978 times)

n7800

  • Hero Member
  • *****
  • Posts: 710
  • Lazarus IDE contributor
    • GitLab profile
I spent the day (with breaks to wait for free Claude requests) quickly writing a small program to show exactly what’s taking up space in the binary

(requires DWARF debug information)

I understand it shows the size only of the debug data? Sorry, I just skimmed the surface.

It would be interesting to know what takes up space in the release file. For example, how much memory is consumed by constants (strings, numbers, arrays) and code, or how much is consumed by the program itself and the RTL (or even individual FPC/Lazarus packages).

The latter would likely require mapping file sections to package units. I'm not very knowledgeable about this, and I'm not sure if this is even possible due to some compiler optimizations...

ALLIGATOR

  • Sr. Member
  • ****
  • Posts: 440
  • I use FPC [main] 💪🐯💪
I understand it shows the size only of the debug data?
No, it doesn't actually show the size of the debug data. But it needs the debug data to understand the boundaries of functions, strings, arrays, and other elements - in order to determine their size.
Debug information does not affect the size of “effective data” - that is, “non-passive” data that the application needs (as opposed to debug information, which can be safely stripped).

It would be interesting to know what takes up space in the release file.
As I mentioned above, debug information does not affect the size of the “effective data” (I can't quite find the right word). Therefore, assuming the same level of optimization, a release build without debug symbols is no different from a debug build with debug symbols. Debug symbols can be stripped. And this utility only needs them to understand function boundaries, etc. - to determine their size.

For example, how much memory is consumed by constants (strings, numbers, arrays) and code, or how much is consumed by the program itself and the RTL (or even individual FPC/Lazarus packages).
A table and a visual map are generated based on individual elements - you can check them there.
As for grouping by units/packages - that’s not available. It probably wouldn’t be possible to group by packages anyway if we rely solely on information from DWARF. As far as I understand, at most, only the unit name is stored there.

Honestly, this is more of a toy, so I haven’t tried to come up with many improvements. Plus, I’m doing this on a free Claude subscription, which means that after almost every task, it triggers a timeout for me lasting ~4–6 hours. That’s why the iterations end up being pretty tedious.

The latter would likely require mapping file sections to package units. I'm not very knowledgeable about this, and I'm not sure if this is even possible due to some compiler optimizations...
👍
I may seem rude - please don't take it personally

Khrys

  • Sr. Member
  • ****
  • Posts: 456
It probably wouldn’t be possible to group by packages anyway if we rely solely on information from DWARF. As far as I understand, at most, only the unit name is stored there.

One should (theoretically) be able to group units by package by cross-referencing source file paths (at least for packages with a known directory structure, e.g.  C:/lazarus/fpc/3.2.2/source/packages/fcl-json/src/fpjson.pp  could reasonably be mapped to  fcl-json  by looking for the  fpc/<VERSION>/source/packages  prefix):

Quote from: DWARF Debugging Standard Wiki
For  DW_TAG_compilation_unit  and  DW_TAG_partial_unit  DIEs, the name attribute should contain the path name of the primary source file from which the compilation unit was derived (see Section 3.1.1). If the compiler was invoked with a full path name, it is recommended to use the path name as given to the compiler, although it is considered acceptable to convert the path name to an equivalent path where none of the components is a symbolic link. If the compiler was invoked with a relative path name, it is recommended to use the relative path name as given to the compiler; a consumer must be able to locate the source file by combining the compilation directory (see  DW_AT_comp_dir)  with the relative path name.

LeP

  • Sr. Member
  • ****
  • Posts: 344
A monolithic executable is also generated by Delphi, which can optionally generate part of the code related to the native Delphi code itself (rtl, vcl, etc.) via DLLs (called BPLs). The resulting code will therefore also be shorter, but obviously these BPLs (which are unique for each version of Delphi) will need to be distributed once. This mechanism only works on Windows.

And the EXE+BPL will be (considerably) larger than everything in one EXE. So if you don't install more than one larger binaries, it is probably not worth it.

See also https://wiki.freepascal.org/packages for some basic analysis of the Delphi BPL systems and how it would work in FPC context.

The screenshots show the size of a monolithic "Hello World" executable and an executable with the necessary BPLs in Delphi.

The "monolithic" executable can be further reduced by not declaring the project's standard uses.

The overall size of the BPL and the executable is reduced compared to the monolithic (at least for a "Hello World").

[EDIT] P.S.: Compile and Build with RELEASE configuration, without debug info.
« Last Edit: May 27, 2026, 01:46:39 pm by LeP »
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

jamie

  • Hero Member
  • *****
  • Posts: 7772
Compiling with debug information attached to the file especially if you got line numbers included is going to make your EXE very large looking.

Currently I have a rather large project and in 64-bit mode the actual Bin size you see on file is no more than 8 MB approximately for an 32-bit mode but the debug file which is external as I have it configured that way is very large.
 This is on windows-64.

I find for the most part laz files are almost twice the size of my old Delphi install.

I am sure recent Delphi is catching up.

P.S.
 A good chuck of that size is from the lfm file attach which only gets referenced but loaded fully in memory.
« Last Edit: May 27, 2026, 01:13:11 pm by jamie »
The only true wisdom is knowing you know nothing

 

TinyPortal © 2005-2018