使用 gcc 标志 -fno-exceptions 进行编译是否会减小可执行二进制文件的大小?
Does compiling with gcc flag -fno-exceptions reduce the size of the executable binary?
在阅读 gcc manual 中有关异常处理和编译器标志 -fno-exceptions
的部分时,我遇到了以下几行:
Exception handling overhead can be measured in the size of the
executable binary, and varies with the capabilities of the underlying
operating system and specific configuration of the C++ compiler. On
recent hardware with GNU system software of the same age, the combined
code and data size overhead for enabling exception handling is around
7%.
我试图通过使用 Ubuntu 20.04 和 gcc 10.3.0 编译一些简单的 C++ 程序(无异常抛出代码)来重现这种开销,有和没有 -fno-exceptions
标志,但不能观察编译后的二进制可执行文件大小的任何差异。
所以我得出结论,手册中引用的句子仅指使用 -fno-exceptions
重新编译 libstdc++ 文件时生成的二进制文件,因为在这种情况下,每次出现 try
, catch
和 throw
将替换为 if
... else
分支。
我对此不是很确定,所以这是我的问题:
a) 使用-fno-exceptions
编译的用户代码仅防止使用关键字try
、catch
和throw
并且不会自行生成更小的二进制文件,对吧?
b) 使用 -fno-exceptions
编译的用户代码仍然会暴露于 libstdc++ 函数抛出的异常,如果这些未使用 -fno-exceptions
进行(重新)编译,对吗?
c) 使用 -fexceptions
(默认值)编译的用户代码确实会因为生成的帧展开信息而产生更大的二进制文件,但只有在实际使用异常时才会这样,对吗?
它可以 减小二进制文件的大小,对于较大的程序通常也是如此。但是,不能保证始终如此。
a) User code being compiled with -fno-exceptions only prevents using the keywords try, catch and throw and does not generate a smaller binary by itself, right ?
不,它肯定对代码生成有影响。但是,异常不会无差别地增加代码大小。必须存在可能涉及要生成的帧展开代码的异常。在展开期间也必须做一些事情(即 non-trivial 析构函数)。如果一个或另一个不存在于给定函数中,那么 -fno-exceptions
不会对该函数产生影响。
例如,使用 -fno-exceptions
.
编译以下内容将清楚地显示更小的代码大小
#include <vector>
void foo(); // could potentially throw.
void bar() {
std::vector<int> v(12); // has non-trivial destructor.
foo();
}
见 godbolt
注意以下每个更改如何消除异常处理代码:
- 将
foo()
的声明更改为 void foo() noexcept;
。
- 在同一 TU 中提供
foo()
的 non-throwing 实现:void foo() {}
- 将向量的构造移动到调用
foo()
之后。
b) User code being compiled with -fno-exceptions can still be exposed to exceptions being thrown from libstdc++ functions, if these have not been (re)compiled with -fno-exceptions, right ?
libstdc++ 抛出的任何异常冒泡到使用 -fno-exceptions
编译的代码中都会导致程序立即终止。如果那算作“暴露”给你,那么是的。
但是,请记住,大部分 libstdc++ 是直接在 headers 中实现的,并且您的编译器标志将应用于库的那部分。
c) User code being compiled with -fexceptions (the default) will indeed produce a larger binary because of the generated frame unwind information, but only when exceptions are actually used, right ?
接近但不完全。代码将在任何 可能 抛出异常的地方发出。这包括对在与正在编译的不同 TU 中定义的 noexcept
的函数的任何调用。
在阅读 gcc manual 中有关异常处理和编译器标志 -fno-exceptions
的部分时,我遇到了以下几行:
Exception handling overhead can be measured in the size of the executable binary, and varies with the capabilities of the underlying operating system and specific configuration of the C++ compiler. On recent hardware with GNU system software of the same age, the combined code and data size overhead for enabling exception handling is around 7%.
我试图通过使用 Ubuntu 20.04 和 gcc 10.3.0 编译一些简单的 C++ 程序(无异常抛出代码)来重现这种开销,有和没有 -fno-exceptions
标志,但不能观察编译后的二进制可执行文件大小的任何差异。
所以我得出结论,手册中引用的句子仅指使用 -fno-exceptions
重新编译 libstdc++ 文件时生成的二进制文件,因为在这种情况下,每次出现 try
, catch
和 throw
将替换为 if
... else
分支。
我对此不是很确定,所以这是我的问题:
a) 使用-fno-exceptions
编译的用户代码仅防止使用关键字try
、catch
和throw
并且不会自行生成更小的二进制文件,对吧?
b) 使用 -fno-exceptions
编译的用户代码仍然会暴露于 libstdc++ 函数抛出的异常,如果这些未使用 -fno-exceptions
进行(重新)编译,对吗?
c) 使用 -fexceptions
(默认值)编译的用户代码确实会因为生成的帧展开信息而产生更大的二进制文件,但只有在实际使用异常时才会这样,对吗?
它可以 减小二进制文件的大小,对于较大的程序通常也是如此。但是,不能保证始终如此。
a) User code being compiled with -fno-exceptions only prevents using the keywords try, catch and throw and does not generate a smaller binary by itself, right ?
不,它肯定对代码生成有影响。但是,异常不会无差别地增加代码大小。必须存在可能涉及要生成的帧展开代码的异常。在展开期间也必须做一些事情(即 non-trivial 析构函数)。如果一个或另一个不存在于给定函数中,那么 -fno-exceptions
不会对该函数产生影响。
例如,使用 -fno-exceptions
.
#include <vector>
void foo(); // could potentially throw.
void bar() {
std::vector<int> v(12); // has non-trivial destructor.
foo();
}
见 godbolt
注意以下每个更改如何消除异常处理代码:
- 将
foo()
的声明更改为void foo() noexcept;
。 - 在同一 TU 中提供
foo()
的 non-throwing 实现:void foo() {}
- 将向量的构造移动到调用
foo()
之后。
b) User code being compiled with -fno-exceptions can still be exposed to exceptions being thrown from libstdc++ functions, if these have not been (re)compiled with -fno-exceptions, right ?
libstdc++ 抛出的任何异常冒泡到使用 -fno-exceptions
编译的代码中都会导致程序立即终止。如果那算作“暴露”给你,那么是的。
但是,请记住,大部分 libstdc++ 是直接在 headers 中实现的,并且您的编译器标志将应用于库的那部分。
c) User code being compiled with -fexceptions (the default) will indeed produce a larger binary because of the generated frame unwind information, but only when exceptions are actually used, right ?
接近但不完全。代码将在任何 可能 抛出异常的地方发出。这包括对在与正在编译的不同 TU 中定义的 noexcept
的函数的任何调用。