众所周知,gcc / ld选项-ffunction-sections -fdata-sections -Wl,--gc-sections
可以减小二进制大小(例如,请参见Query on -ffunction-section & -fdata-sections options of gcc的答案)。众所周知,链接时间优化可以减少二进制文件的大小和执行速度(例如,参见https://wiki.debian.org/LTO)。
但是,将部分垃圾回收和链接时间优化结合起来会如何呢?链接时间优化不应该使节垃圾收集多余吗?
我用mingw-w64 / g ++ 9.2.0编译了Open CASCADE(https://www.opencascade.com/),具有不同的编译器/链接器选项,并且只是比较了二进制大小。打开CASCADE是一组多个dll。如果仅使用-flto
进行编译,则大多数dll最小。
相反,使用-flto -ffunction-sections -fdata-sections -Wl,--gc-sections
在大多数情况下会略微增加二进制文件的大小。但是,当添加垃圾回收选项时,有些dll实际上会变得略小。
为什么会这样?垃圾回收可能会删除哪些内容,而裸机链接时间优化无法删除哪些内容?是否有最佳实践,可以使用编译/链接选项来最小化二进制文件大小?
备注:我假设只有-flto
会改变执行时间(希望使二进制文件更快!),链接时节垃圾回收很可能会保留执行时间。我还没有时间来衡量。
编辑:为了使所描述的行为与现实的共享库可重现,我创建了一个bash脚本来构建带有两个不同参数集的Open CASCADE。第一行中的注释描述了如何修改脚本。我在Windows的MSYS2控制台中在这里使用它。
#!/bin/bash
# This script requires at least CMake 3.13, otherwise the options "-S" and "-B" do not work as expected.
# Further it needs a mingw-w64 installation in the path.
# Path to the folder that has been downloaded and extracted from https://www.opencascade.com/sites/default/files/private/occt/OCC_7.3.0_release/opencascade-7.3.0.tgz
OCCT_PATH="/c/Libraries/opencascade-7.3.0"
# Path to the folder that has been downloaded and extracted from https://www.opencascade.com/sites/default/files/private/occt/3rdparty/freetype-2.6.3-mingw-64.7z
FREETYPE_PATH="/c/Libraries/freetype-2.6.3-mingw-64"
TEST_DIR="/c/Libraries/Test"
# Here the preparations are done... the script will (hopefully) do the rest.
if [ ! -d "$TEST_DIR" ]; then
mkdir "$TEST_DIR"
fi
# Build with link-time optimization
BUILD_DIR_LTO="BuildLto"
INSTALL_DIR_LTO="InstallLto"
CMAKE_C_FLAGS_LTO="-O2 -DNDEBUG -flto"
CMAKE_CXX_FLAGS_LTO="$CMAKE_C_FLAGS_LTO -std=gnu++17"
# According to https://stackoverflow.com/a/31688314/2492801 neither -flto nor an optimization flag is required with new gcc versions
CMAKE_SHARED_LINKER_FLAGS_LTO=""
cmake -DCMAKE_BUILD_TYPE="Release" -S "$OCCT_PATH" -B "$TEST_DIR/$BUILD_DIR_LTO" -G "MSYS Makefiles" -DINSTALL_DIR="$TEST_DIR/$INSTALL_DIR_LTO" -DCMAKE_CXX_FLAGS_RELEASE="$CMAKE_CXX_FLAGS_LTO" -DCMAKE_C_FLAGS_RELEASE="$CMAKE_C_FLAGS_LTO" -DCMAKE_SHARED_LINKER_FLAGS_RELEASE="$CMAKE_SHARED_LINKER_FLAGS_LTO" -DBUILD_MODULE_Draw=False -D3RDPARTY_FREETYPE_DIR="$FREETYPE_PATH"
cd "$TEST_DIR/$BUILD_DIR_LTO"
mingw32-make -j 24 install
# Build with link-time optimization and link-time section garbage collection
BUILD_DIR_GC="BuildLtoGc"
INSTALL_DIR_GC="InstallLtoGc"
CMAKE_C_FLAGS_GC="-O2 -DNDEBUG -flto -ffunction-sections -fdata-sections"
CMAKE_CXX_FLAGS_GC="$CMAKE_C_FLAGS_GC -std=gnu++17"
# According to https://stackoverflow.com/a/31688314/2492801 neither -flto nor an optimization flag is required with new gcc versions
CMAKE_SHARED_LINKER_FLAGS_GC="-Wl,--gc-sections"
cmake -DCMAKE_BUILD_TYPE="Release" -S "$OCCT_PATH" -B "$TEST_DIR/$BUILD_DIR_GC" -G "MSYS Makefiles" -DINSTALL_DIR="$TEST_DIR/$INSTALL_DIR_GC" -DCMAKE_CXX_FLAGS_RELEASE="$CMAKE_CXX_FLAGS_GC" -DCMAKE_C_FLAGS_RELEASE="$CMAKE_C_FLAGS_GC" -DCMAKE_SHARED_LINKER_FLAGS_RELEASE="$CMAKE_SHARED_LINKER_FLAGS_GC" -DBUILD_MODULE_Draw=False -D3RDPARTY_FREETYPE_DIR="$FREETYPE_PATH"
cd "$TEST_DIR/$BUILD_DIR_GC"
mingw32-make -j 24 install
文件Test/BuildLto/win64/gcc/bin/libTKGeomAlgo.dll
的大小为4378624字节,但是Test/BuildLtoGc/win64/gcc/bin/libTKGeomAlgo.dll
的大小为4377600字节。因此,额外的链接时垃圾回收不仅减少了链接时优化,还减少了文件大小。为什么?
备注:如前所述,这是一个例外。链接时垃圾回收更多地使结果共享库更大。