46

在精神上,我一直想知道当 C++ 编译将其转换为汇编程序时,try/throw/catch 在幕后看起来如何。但是因为我从不使用它,所以我从来没有时间检查它(有些人会说懒惰)。

是用于跟踪trys 的普通堆栈,还是单独为此目的而保留的每个线程堆栈?MSVC 和 g++ 之间的实现是大还是小?请给我看一些伪 asm(IA-32 也可以),这样我就不必自己检查了!:)

编辑:现在我了解了 MSVC 在 IA-32 处理方面的实现基础。有人知道 IA-32 上的 g++ 或任何其他 CPU 吗?

编辑 2(11 年后): 以下是一些性能数据。他们还免费提供源代码

4

3 回答 3

26

异常处理程序的不良实现会在进入 try 子句时为运行时堆栈上的每个 try 子句推送某种异常处理程序块,并在退出 try 子句时将其弹出。保存最近推送的异常处理程序块的地址的位置也被维护。通常,这些异常处理程序链接在一起,因此可以通过从最新版本到旧版本的链接找到它们。当发生异常时,会找到指向最后推送的 EH 处理程序块的指针,并检查该“try”子句的 EH 案例的处理。对 EH 案例的命中会导致堆栈清理回到被推送的 EH 点,并且控制转移到 EH 案例。没有命中 EH 会导致找到下一个 EH,并重复该过程。Windows 32 位 SEH 方案是该方案的一个版本。

这是一个糟糕的实现,因为即使没有发生异常,程序也会为每个 try 子句(push 然后 pop)支付运行时代价。

好的实现只是记录一个出现 try 子句的范围表。这意味着进入/退出 try 子句的开销为零。(我的PARLANSE并行编程语言使用了这种技术)。异常在表中查找异常点的PC,并将控制权交给表选择的EH。EH 代码会根据需要重置堆栈。又快又漂亮。我认为Windows 64位EH就是这种类型,但我没有仔细看。

[编辑 2020 年 4 月:最近刚刚测量了 PARLANSE 异常的成本。如果没有例外,则为 0nS(按设计);在 3Ghz i7 上从“抛出”到“捕获”再到“确认”(结束空捕获)需要 25ns。OP 为最简单的类型添加了一个测量 C++ 异常处理大约 1000ns 的链接,以及一个字面上的非标准处理方案,该方案以 57ns 的时间处理异常或无异常;C++ 版本的 CPU 时钟速率稍慢,因此这些数字仅用于粗略比较。]

于 2009-08-25T22:05:01.050 回答
5

C++ 标准委员会发布了一份关于“C++ 性能”的技术报告,以揭穿许多关于 C++ 特性如何减慢你速度的神话。这还包括有关如何实现异常处理的详细信息。本技术报告草稿可免费获取。检查第 5.4.1 节。《异常处理实现问题与技术》。

于 2009-10-03T06:37:44.277 回答
2

来自Godbolt 编译器资源管理器的Asm ,用于 x86-64 System V 调用约定与 g++8.2 的 C++ABI,用于捕获的函数和抛出的函数。

x86-64 System V 使用.eh_frame堆栈展开元数据部分,因此异常帮助程序库函数知道如何遍历堆栈和恢复寄存器。这就是.cfi指令的作用。

于 2018-08-03T08:57:56.520 回答