35

为什么嵌入式平台开发人员不断尝试C++ exceptions从他们的SDKs.

例如,Bada SDK针对异常使用建议以下解决方法,它看起来异常难看:

 result
 MyApp::InitTimer()
 {
    result r = E_SUCCESS;

    _pTimer = new Timer;

    r = _pTimer->Construct(*this);
    if (IsFailed(r))
    {
        goto CATCH;
    }

    _pTimer->Start(1000);
    if (IsFailed(r))
    {
        goto CATCH;
    }

    return r;
 CATCH:
     return r;
 }

这种行为的原因是什么?

据我所知,ARM编译器完全支持C++ exceptions,这实际上不是问题。还有什么?平台上的异常使用和展开的开销ARM真的要花很多时间来解决这些问题吗?

也许还有其他我不知道的东西?

谢谢你。

4

6 回答 6

56

只是我的2美分...

我专门咨询嵌入式系统,其中大多数是硬实时和/或安全/生命攸关的。它们中的大多数运行在 256K 或更少的闪存/ROM 中 - 换句话说,这些不是具有 1GB+ RAM/闪存和 1GHz+ CPU 的“类似 PC”的 VME 总线系统。它们是深度嵌入、资源有限的系统。

我想说至少 75% 的使用 C++ 的产品在编译器中禁用异常(即,使用禁用异常的编译器开关编译的代码)。我总是问为什么。信不信由你,最常见的答案不是运行时或内存开销/成本。

答案通常是以下几种:

  • “我们不确定我们知道如何编写异常安全代码”。对他们来说,检查返回值更熟悉、更简单、更安全。
  • “假设你只在特殊情况下抛出异常,这些情况下我们无论如何都要重启 [通过他们自己的严重错误处理程序例程]”
  • 遗留代码问题(正如 jalf 所提到的) - 他们正在使用多年前开始的代码,当时他们的编译器不支持异常,或者没有正确或有效地实现它们

此外 - 经常有一些关于开销的模糊不确定性/恐惧,但几乎总是它是未量化/未分析的,它只是一种被扔在那里并按面值计算。我可以向您展示声明异常开销为 3%、10%-15% 或 ~30% 的报告/文章 - 任您选择。人们倾向于引用代表自己观点的人物。几乎总是,文章已经过时,平台/工具集完全不同,等等。正如罗迪所说,你必须在你的平台上衡量自己。

我不一定要为这些立场辩护,我只是给你真实世界的反馈/解释,我从许多在嵌入式系统上使用 C++ 的公司那里听到,因为你的问题是“为什么这么多嵌入式开发人员避免例外?”

于 2011-07-13T15:11:23.803 回答
19

我能想到几个可能的原因:

  • 旧版本的编译器不支持异常,因此编写了很多代码(并建立了约定),其中不使用异常
  • 异常确实是有代价的,它可能占总执行时间的 10-15%(它们也可以实现为几乎不花时间,但会使用相当多的内存,这可能不是很理想在嵌入式系统上)
  • 嵌入式程序员往往对代码大小、性能,尤其是代码复杂性有点偏执。他们经常担心“高级”功能可能无法在他们的编译器中正常工作(而且他们通常也是正确的)
于 2011-07-13T11:13:07.037 回答
8

我认为这主要是 FUD,这些天。

异常确实在创建具有构造函数/析构函数的对象的块的入口和出口处有少量开销,但在大多数情况下,这实际上不应该相当于一罐豆子。

先测量,再优化。

然而,抛出异常通常比仅仅返回一个布尔标志要慢,所以只为异常事件抛出异常

在一种情况下,我看到 RTL 正在从符号表构造整个可打印的堆栈跟踪,只要抛出异常以供潜在的调试使用。可以想象,这不是一件好事。这是几年前的事了,当这个问题曝光时,调试库被匆忙修复。

但是,IMO,您从正确使用异常中获得的可靠性远远超过了轻微的性能损失。使用它们,但要小心。

编辑:

@jalf 提出了一些很好的观点,我上面的回答针对的是为什么许多嵌入式开发人员通常仍然贬低例外的相关问题。

但是,如果特定平台 SDK 的开发人员说“不要使用异常”,您可能不得不这样做。也许他们的库或编译器中的异常实现存在特殊问题 - 或者他们担心回调中抛出的异常会由于他们自己的代码中缺乏异常安全性而导致问题。

于 2011-07-13T11:21:52.160 回答
5

这种对异常的态度与性能或编译器支持无关,而是与异常增加代码复杂性的想法有关。

据我所知,这个想法几乎总是一种误解,但由于某些不可思议的原因,它似乎有强大的支持者。

于 2011-07-13T11:49:44.627 回答
5

与其他答案所支持的“gotos are evil”相反的观点。我正在制作这个社区维基,因为我知道这种相反的观点会被激怒。

任何称职的实时程序员都知道goto. 它是一种广泛使用且被广泛接受的错误处理机制。许多硬实时编程环境没有实现<setjmp.h>。例外在概念上只是setjmp和的受限版本longjmp。那么,当底层机制被禁止时,为什么要提供异常呢?

如果始终可以保证在本地处理所有抛出的异常,则环境可能允许异常。问题是,为什么要这样做?唯一的理由是 gotos 总是邪恶的。好吧,他们并不总是邪恶的。

于 2011-07-13T11:55:09.460 回答
4

现代 C++ 编译器可以将异常的运行时使用减少到 3% 的开销。尽管如此,如果极端程序员发现它很昂贵,那么他们就会诉诸这种肮脏的伎俩。

请参阅此处 Bjarne Strourstrup 的页面,了解为什么使用异常?

于 2011-07-13T11:21:37.950 回答