如何减小可执行文件的大小?

dig*_*evo 3 c++ executable gcc fmt

当我使用 lib 编译此代码时{fmt},可执行文件大小变为 255 KiB,而仅使用iostream标头则变为 65 KiB(使用 GCC v11.2)。

time_measure.cpp

#include <iostream>
#include "core.h"
#include <string_view>

int main( )
{
    // std::cout << std::string_view( "Oh hi!" );
    fmt::print( "{}", std::string_view( "Oh hi!" ) );

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是我的构建命令:

g++ -std=c++20 -Wall -O3 -DNDEBUG time_measure.cpp -I include format.cc -o runtime_measure.exe
Run Code Online (Sandbox Code Playgroud)

与 相比,这个库不{fmt}应该是轻量级的吗iostream?或者也许我做错了什么?

编辑:通过添加-s命令以从可执行文件中删除所有符号表和重定位信息,它变为 156 KiB。但仍然比版本多约 2.5 倍iostream

vit*_*aut 6

与任何其他图书馆一样,有固定成本和每次调用成本。如果没有调试信息,{fmt} 库的固定成本确实约为 100-150k(这取决于编译器标志)。在您的示例中,您正在比较与库链接的固定成本,而 iostreams 看起来较小的原因是因为它包含在标准库本身中,该库本身是动态链接的,并且不计入可执行文件的二进制大小。

请注意,此大小的很大一部分来自浮点格式化功能,该功能甚至不存在于 iostream(最短往返表示)中。

如果您想比较每次调用的二进制大小(这对于具有大量格式化函数调用的实际代码更为重要),您可以查看目标文件或生成的程序集。例如:

#include <fmt/core.h>

int main() {
  fmt::print("Oh hi!");
}
Run Code Online (Sandbox Code Playgroud)

生成(https://godbolt.org/z/qWTKEMqoG

.LC0:
        .string "Oh hi!"
main:
        sub     rsp, 24
        pxor    xmm0, xmm0
        xor     edx, edx
        mov     edi, OFFSET FLAT:.LC0
        mov     rcx, rsp
        mov     esi, 6
        movaps  XMMWORD PTR [rsp], xmm0
        call    fmt::v8::vprint(fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<fmt::v8::appender, char> >)
        xor     eax, eax
        add     rsp, 24
        ret
Run Code Online (Sandbox Code Playgroud)

尽管

.LC0:
        .string "Oh hi!"
main:
        sub     rsp, 24
        pxor    xmm0, xmm0
        xor     edx, edx
        mov     edi, OFFSET FLAT:.LC0
        mov     rcx, rsp
        mov     esi, 6
        movaps  XMMWORD PTR [rsp], xmm0
        call    fmt::v8::vprint(fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<fmt::v8::appender, char> >)
        xor     eax, eax
        add     rsp, 24
        ret
Run Code Online (Sandbox Code Playgroud)

生成(https://godbolt.org/z/frarWvzhP

.LC0:
        .string "Oh hi!"
main:
        sub     rsp, 8
        mov     edx, 6
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        xor     eax, eax
        add     rsp, 8
        ret
_GLOBAL__sub_I_main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        add     rsp, 8
        jmp     __cxa_atexit
Run Code Online (Sandbox Code Playgroud)

除了静态初始化之外,cout没有太大区别,因为这里实际上没有格式化,因此在两种情况下都只是一个函数调用。添加格式后,您将很快看到 {fmt} 的好处,请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r10.html#BinaryCode