如果在引发异常时无法承受缓慢的性能,可以使用STL吗?

时间:2020-03-06 15:02:53  来源:igfitidea点击:

例如,我正在编写一个多线程的时间关键型应用程序,该应用程序实时处理和流式传输音频。音频中断是完全不能接受的。这是否表示我无法使用STL,因为抛出异常时可能会减慢速度?

解决方案

通常,如果new失败,STL容器将自己抛出的唯一异常是std :: bad_alloc。仅有的其他时间是用户代码(例如,构造函数,赋值,复制构造函数)抛出时。如果用户代码从不抛出,那么我们只需要防止新的抛出,就很可能必须这样做。

可能引发异常的其他事项:
如果我们无法访问at()函数,则它们可能会抛出std :: out_of_range。无论如何,这是一个严重的程序错误。

其次,例外并不总是很慢。如果音频处理中发生异常,则可能是由于我们必须要处理的严重错误所致。错误处理代码可能比将异常传输到捕获站点的异常处理代码要昂贵得多。

如果抛出STL容器,则可能比减速慢得多:)

我正在努力考虑STL的哪些部分指定它们可以引发异常。以我的经验,大多数错误处理都是由返回码处理或者作为使用STL的前提条件。
传递给STL的对象肯定会引发异常,例如复制构造函数,但是无论使用STL还是一个​​问题。
其他人提到了诸如std :: vector :: at()之类的函数,但是我们通常可以执行检查或者使用其他方法来确保不会引发异常。

当然,STL的特定实现通常可以在我们使用STL时执行"检查"(通常用于调试版本),我认为它只会引发一个断言,但也许某些情况会引发异常。

如果没有try / catch,除非我们自己的类引发异常,否则我相信不会/会降低性能。

在Visual Studio上,我们可以完全禁用C ++异常,请参见"项目属性-> C / C ++->代码生成->启用C ++异常"。我认为这在大多数C ++平台上都可用。

不要害怕性能方面的例外。

在C ++的早期,在某些编译器上,启用了例外的构建可能会慢很多。

这些天,无论构建是否带有异常处理,都已变得无关紧要。

通常,除非内存不足,否则STL不会引发异常,因此这对应用程序类型也不成问题。

(现在,请勿在GC中使用某种语言.....)

你说的好像例外是不可避免的。根本不做任何可能导致异常的事情-修复错误,验证输入。

值得注意的几点:

  • 应用程序是多线程的。如果一个线程(可能是GUI线程)因异常而变慢,则不应影响实时线程的性能。
  • 例外是在特殊情况下。如果实时线程中抛出异常,则很可能意味着我们将无法继续播放音频。如果出于某种原因发现我们正在这些线程中继续处理异常,请重新设计以避免出现异常。

我建议我们接受带有例外的STL(除非STL本身证明速度太慢,但请记住:首先进行测量,然后进行优化),并针对我们自己的"例外情况"(音频硬件故障,无论如何)采用例外处理你的申请。

先前的答案中并未明确写明,因此:

C ++中发生异常

是否使用STL不会删除将释放我们分配的对象资源的RAII代码。

例如:

void doSomething()
{
    MyString str ;
    doSomethingElse() ;
}

在上面的代码中,无论在此期间发生什么情况(包括如果doSomethingElse引发异常还是我们之前执行"返回"),编译器都会生成代码以释放MyString资源(即将调用MyString析构函数)函数范围的末尾。

如果我们对此有疑问,则可以修改心态或者尝试C。

异常应该是例外

通常,当发生异常时(且仅在发生异常时),我们会受到性能影响。

但是,只有在以下情况下才应发送异常:

  • 我们有一个特殊事件要处理(即某种错误)
  • 在非常特殊的情况下(例如,从堆栈中的多个函数调用"大量返回",例如进行复杂的搜索或者在线程正常中断之前展开堆栈)

这里的关键字是" exceptional",这很好,因为我们正在讨论" exception"(请参见模式?)。

在情况下,如果引发了异常,则很有可能发生某些不好的事情,程序无论如何都会崩溃而无一例外。

在这种情况下,问题不在于解决性能问题。这是为了妥善处理错误,或者更糟糕的是,程序会正常终止(包括"对不起"消息框,将未保存的数据保存到临时文件中以供以后恢复等)。

这意味着(除非在非常例外的情况下),请勿将异常用作"返回数据"。当某些非常糟糕的事情发生时,抛出异常。仅在我们知道如何处理时才捕获异常。避免尝试/捕获(除非我们知道如何处理异常)。

STL呢?

现在我们知道了:

  • 我们仍然想使用C ++
  • 目标不是为了娱乐而每秒钟抛出数千个异常

我们应该讨论STL:

STL(如果可能)通常会验证我们是否做错了什么。如果这样做,它将引发异常。不过,在C ++中,我们通常不会为不使用的东西付费。

例如访问矢量数据。

如果知道不会超出范围,则应使用运算符[]。

如果我们知道不验证边界,则应使用at()方法。

范例A:

typedef std::vector<std::string> Vector ;

void outputAllData(const Vector & aString)
{
   for(Vector::size_type i = 0, iMax = aString.size() ; i != iMax ; ++i)
    {
       std::cout << i << " : " << aString[i] << std::endl ;
    }
}

范例B:

typedef std::vector<std::string> Vector ;

void outputSomeData(const Vector & aString, Vector::size_type iIndex)
{
   std::cout << iIndex << " : " << aString.at(iIndex) << std::endl ;
}

该示例"信任"程序员,并且不会浪费任何时间进行验证(因此,如果仍然存在错误,那么在那时抛出异常的机会就会减少……这通常意味着错误/异常/崩溃通常会发生)发生之后,这将不利于调试,并使更多数据遭到破坏)。

示例B要求向量验证索引正确,如果不正确,则引发异常。

这是你的选择。