【问题标题】:C++ exception overheadC++ 异常开销
【发布时间】:2011-10-04 09:49:39
【问题描述】:

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

例如,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 平台上的异常使用和展开的开销是否真的BIG 需要花费大量时间来解决这些问题?

也许还有一些我不知道的事情?

谢谢。

【问题讨论】:

  • +1 将其描述为异常丑陋...
  • 一大原因是旧代码。除非代码从一开始就写成异常安全的,否则它不是异常安全的。这是 Google 不使用异常的主要原因之一:一开始没有,现在我们有点坚持这个决定。
  • 我建议将“使用”标签(对我来说似乎是无操作)更改为“嵌入”。
  • 您的意思是“为什么他们不允许平台中的异常”或“人们为什么不使用异常”?对于前者,禁用异常是确保与使用“嵌入式 C++”子集的平台兼容的途径。 en.wikipedia.org/wiki/Embedded_C%2B%2B
  • 查看许多答案here。有一篇专门关于exceptions 的帖子。 setjmp()longjmp() 受到更多控制。每个对象经常被输入到异常表中,并且在每个文件编译中找出表是非最佳的。通常,如果它位于磁盘上,这并不痛苦。嵌入式应用程序通常没有磁盘。即使在今天(2013 年),g++ 开发人员仍在尝试优化这些表。在某些情况下,它们可能与代码一样大!

标签: c++ exception embedded arm overhead


【解决方案1】:

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

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

【讨论】:

  • 您能提供 10-15% 值的参考吗?还是说它们占用了很多内存?
  • @edA-qa C++ 性能报告:www2.research.att.com/~bs/performanceTR.pdf,尽管它给出了不同的数字。
  • 哪一部分包含异常处理的编号?在 2.4 中,它们说明了差异/细节,但似乎没有给出实际的比较数字。
  • @edA:请注意,我并没有说“他们总是会接受”,只是说可以这么多。我这里没有源代码,但是是的,我已经看到一些基准测试显示在该范围内的性能受到影响。但它显然很多取决于具体的实现。我不是说“异常很慢”,而是“异常可能很慢”。至于“很多内存”部分,这是真的,对于“很多”的合适定义。基于表的方法(基本上将指令指针值映射到包含静态异常信息的表条目)占用空间,以避免速度下降
  • @KillianDS:据我们所知,这可能是由开发人员讨论异常开销引起的“开发时间开销”。
【解决方案2】:

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

在此处查看 Bjarne Strourstrup 的页面,Why use Exceptions ?

【讨论】:

  • 我几乎不是嵌入式平台的世界级专家,但我相信他们很少有可以被视为“现代 C++ 编译器”的东西,这使得 3% 的数字相当无关紧要。 :)
【解决方案3】:

我认为现在主要是 FUD。

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

先测量,再优化。

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

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

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

编辑:

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

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

【讨论】:

  • "所以只为异常事件抛出异常。" +1
  • 请记住,我们讨论的是嵌入式平台,其中异常的实现可能不如在更主流的平台上优化。
  • 另外,你没有回答这个问题。这不是“gotos 是邪恶的,还是应该使用异常”,而是“为什么许多嵌入式 SDK 不鼓励使用异常?”
  • @jalf:“问题”实际上是“C++ 异常开销”,这可能不是很有帮助:-( 不过,关于平台问题的公平点。将编辑...
  • “先测量,再优化。” +1
【解决方案4】:

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

据我所知,这个想法几乎总是一个误解,但它似乎有powerful proponents 出于某种不可思议的原因。

【讨论】:

  • 还有,Joel SpolskyJoel Spolskyhas lost his mind 系列的一部分)
  • 异常会增加代码的复杂性,如果复杂性是通过圈复杂度来衡量的。如果计算正确,函数抛出的每种异常类型都会将该函数的圈复杂度增加两倍。
  • @DavidHammen 您需要将异常与其他错误报告方案进行比较,而不是什么都没有的异常。
【解决方案5】:

与其他答案所支持的“gotos 是邪恶的”相反的观点。我制作这个社区 wiki 是因为我知道这种相反的观点会被激怒。

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

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

【讨论】:

  • 我想这也有一定的意义。我认识一些有相同观点的才华横溢的程序员,虽然我可能不同意你的观点,但不同的观点总是有用的。
  • 按照这种逻辑,C++ 不应该提供函数调用或控制逻辑。因为它们基本上只是“智能”跳跃,又名 goto,又名“禁止”
  • 嗯,异常实际上只是 goto 的抽象,具有一路自动 RAII 清理的优势 :)
  • 什么 RAII 清理?这是一个关于实时编程环境的问题。这里没有 auto_ptr、向量、C++ 流之类的东西。当您切换到实时编程模式时,您必须抛弃许多标准编程模式的思维方式。
  • @大卫。实时是一个广泛的教会:30 多年以来,我一直在用汇编程序、pascal、c、c++ 编写从 PIC 开始的硬实时系统。您可以使用对您的要求有意义的语言功能来简化工作。在许多情况下,RAII 和模板是组合的一部分。
【解决方案6】:

只要我的 2 美分...

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

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

答案通常是以下几种:

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

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

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

【讨论】:

  • 我的想法和经验也正朝着这个方向前进,并补充说,添加到项目中的任何代码/二进制文件都会增加风险。如果从代码中增加的价值不能弥补风险和验证代码所需的质量保证周期,那么就不要添加它。嵌入式系统希望比桌面系统更可靠,任何和每一行代码或库 blob 链接,使用与否,都会增加您的风险。
猜你喜欢
  • 2010-09-07
  • 2012-06-13
  • 2012-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-09
相关资源
最近更新 更多