Dec 1, 2024

[C++][cpponsea] Keep C++ Binaries Small - summary

Reference:
https://youtu.be/7QNtiH5wTAs?si=c8I_prA9k9Hasfdr


Problem

  • Devices with limited storage
  • Devices with limited bandwith
  • Env impact

Parts of an executable

  • Header
  • Text
  • Data (init. vs uninit)
  • Read-only data
  • symbol table
  • Relocation info
  • (ref https://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking)
  • Import table / Procedure linkage table
  • Export table
  • Exception handling info
  • Debugging info

Consider

  • cross platform?
  • linking/symbol resolution handling
  • PIC
  • Endianness
  • Architecture / extensibility

Tools


Coding part


Object init.

0(.zerofill) vs. other thing else; decrease size


Member ordering

Usually won't reduce the binary size due to compiler is able to optimize.


Special member functions

  • inline default special functions
  • inline empty special functions
  • following the rule of zero(RoZ)
  • with either virtual or non-virtual destructor


templates

  • minimal template code section; same as marco
  • Exact/promote to the upper level, which the code is not generated
    for every new type instance.

Compiler options

  • -Os
  • -flto
  • -fdata-sections / -ffunction-sections -Wl, --gc-sections
    • The Traditional Way (Still Valid) If you are linking against a traditional static library (.a file), the linker still operates on an object-file level. If a static library is built with strlen.o, strcpy.o, and printf.o as separate files inside the archive, and your code only references strlen, the linker will strictly pull in strlen.o. The other object files are completely ignored, keeping your binary small. Many embedded and legacy C libraries are still structured exactly this way.
    • The Modern Way: Section Garbage Collection Today, instead of splitting source code into a million files, we let the compiler and linker do the heavy lifting using function-level sections. When compiling modern C code (using GCC or Clang), you can pass specific flags: -ffunction-sections and -fdata-sections: This tells the compiler to put every single function and data item into its own distinct section inside a single object file, rather than bundling them all into one giant .text section. --gc-sections (Linker flag): This tells the linker to perform "garbage collection" and throw away any unused sections during the final build. Why this matters: You can have a single string.c file containing 50 functions. With these flags, the linker will still grab only strlen and discard the other 49, achieving the exact same tiny binary size without the architectural headache.
    • Link-Time Optimization (LTO) The ultimate evolution of this is LTO (compiled with -flto). With LTO enabled, the compiler doesn't just emit machine code into object files; it emits its internal intermediate representation. At the linking stage, the compiler looks at the entire program holistically. It can inline strlen directly into your code, eliminate dead code with extreme precision, and completely optimize away unused functions, regardless of how the source files or libraries were structured.
  • -Wl, --icf=all or -Wl, --icf==safe
  • -Wl, -s and -Wl, --strip-all
  • -Wl, --as-needed
  • -mllvm -inline-threshold=<n>
  • -fstack-protector
    https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fstack-protector
  • -finline-limit=n
    https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-finline-limit
  • Identical Code Folding (ICF)
    https://research.google/pubs/safe-icf-pointer-safe-and-unwinding-aware-identical-code-folding-in-gold

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.