【问题标题】:Does compiling with gcc flag -fno-exceptions reduce the size of the executable binary?使用 gcc 标志 -fno-exceptions 编译会减小可执行二进制文件的大小吗?
【发布时间】:2021-12-03 03:20:30
【问题描述】:

在阅读gcc manual 中关于异常处理和编译器标志-fno-exceptions 的部分时,我遇到了以下几行:

异常处理开销可以用 可执行二进制文件,并随底层的功能而变化 操作系统和 C++ 编译器的特定配置。在 最近的硬件与同龄的 GNU 系统软件相结合, 启用异常处理的代码和数据大小开销约为 7%。

我试图通过使用 Ubuntu 20.04 和 gcc 10.3.0 编译一些简单的 C++ 程序(没有异常抛出代码)来重现这种开销,无论是否带有 -fno-exceptions 标志,但无法观察到任何关于大小的差异编译后的二进制可执行文件。

所以我得出的结论是,手册中引用的句子仅指使用 -fno-exceptions 重新编译 libstdc++ 文件时生成的二进制文件,因为在这种情况下,trycatch 和 @ 的每次出现987654328@ 将替换为 if ... else 分支。

我对此并不完全确定,所以这是我的问题:

a) 使用-fno-exceptions 编译的用户代码只会阻止使用关键字trycatchthrow,并且不会自行生成更小的二进制文件,对吧?

b) 使用 -fno-exceptions 编译的用户代码仍然可以暴露于 libstdc++ 函数抛出的异常,如果这些函数没有使用 -fno-exceptions (重新)编译,对吧?

c) 使用-fexceptions(默认值)编译的用户代码确实会因为生成的帧展开信息而产生更大的二进制文件,但只有在实际使用异常时,对吧?

【问题讨论】:

  • 您的“简单 C++ 程序”是否有任何抛出异常的代码?
  • @Frank :不,看我的编辑
  • 如果程序没有任何潜在异常抛出代码,那么从可执行文件中剥离所有异常处理代码是一种简单的优化,不会有任何区别。但是,使用 RAII 和不使用 LTO 编译的多 TU 程序肯定会显示出差异,因为编译器无法判断 extern 函数是否可以抛出。
  • 这里有一个简单的例子来展示差异:gcc.godbolt.org/z/voGTYcbG6(请注意,LTO 稍后可能仍会在工具链中优化差异)

标签: c++ exception gcc


【解决方案1】:

可以减小二进制文件的大小,这通常适用于较大的程序。但是,不能保证总是这样做。

a) 使用 -fno-exceptions 编译的用户代码只会阻止使用关键字 try、catch 和 throw,并且不会自行生成更小的二进制文件,对吧?

不,它肯定会对代码生成产生影响。但是,异常不会不加选择地增加代码大小。要生成帧展开代码,必须存在可能涉及的异常。在展开期间还必须做一些事情(即非平凡的析构函数)。如果给定函数中不存在其中一个或另一个,则-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() 的非抛出实现:void foo() {}
  • 将向量的构造移动到调用foo() 之后。

b) 使用 -fno-exceptions 编译的用户代码仍然可以暴露于 libstdc++ 函数抛出的异常,如果这些异常没有使用 -fno-exceptions 进行(重新)编译,对吧?

libstdc++ 抛出的任何异常冒泡到使用-fno-exceptions 编译的代码中将导致程序立即终止。如果这算作“暴露”给你,那么是的。

但是,请记住,大部分 libstdc++ 直接在标头中实现,并且您的编译器标志将应用于库的该部分。

c) 使用 -fexceptions(默认值)编译的用户代码确实会生成更大的二进制文件,因为生成的帧展开信息,但只有在实际使用异常时,对吧?

关闭但不完全。代码将在可能抛出异常的任何地方发出。这包括对没有 noexcept 的函数的任何调用,该函数定义在与正在编译的不同的 TU 中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    • 2010-09-16
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    相关资源
    最近更新 更多