views:

770

answers:

7

For example, I'm writing a multi-threaded time-critical application that processes and streams audio in real-time. Interruptions in the audio are totally unacceptable. Does this mean I cannot use the STL because of the potential slow down when an exception is thrown?

+27  A: 

Generally, the only exceptions that STL containers will throw by themselves is an std::bad_alloc if new fails. The only other times are when user code (for example constructors, assignments, copy constructors) throws. If your user code never throws then you only have to guard against new throwing, which you would have had to do anyways most likely.

Other things that can throw exceptions: - at() functions can throw std::out_of_range if you access them out of bounds. This is a serious program error anyways.

Secondly, exceptions aren't always slow. If an exception occurs in your audio processing, its probably because of a serious error that you will need to handle anyways. The error handling code is probably going to be significantly more expensive than the exception handling code to transport the exception to the catch site.

Greg Rogers
+7  A: 

If an STL container throws, you are probably having much bigger problem than the slow down :)

Nemanja Trifunovic
+1  A: 

I'm struggling to think which portions of the STL specify that they can raise an exception. In my experience most error handling is handled by return codes or as a prerequisite of the STL's use. An object passed to the STL could definitely raise an exception, e.g. copy constructor, but that would be an issue regardless of the use of STL. Others have mentioned functions such as std::vector::at() but you can perform a check or use an alternate method usually to ensure no exception can be thrown.

Certainly a particular implementation of the STL can performs "checks", generally for debug builds, on your use of the STL, I think it will raise an assertion only, but perhaps some will throw an exception.

If there is no try/catch present I believe no/minimal performance hit will be incurred unless an exception is raised by your own classes.

On Visual Studio you can disable the use of C++ exceptions entirely see Project Properties -> C/C++ -> Code Generation -> Enable C++ Exceptions. I presume this is available on most C++ platforms.

Henk
+3  A: 

Do not be afraid of exceptions with regard to performance.

In the old days of C++ a build with exceptions enabled could be a lot slower on some compilers.

These days it really does not matter if your build with or without exception handling.

In general STL does not throw exceptions unless you run out of memory so that should not be a problem for your type of application either.

(Now do not use a language with GC.....)

James Dean
A: 

You talk as if exceptions are inevitable. Simply don't do anything that could cause an exception -- fix your bugs, verify your inputs.

James Curran
And write your own memory allocator.
Steve Jessop
@steve using placement-new `new (std::nothrow)` is much simpler approach. There's no need to write the memory allocator again, it's not his fault...
Elazar Leibovich
@Elazar: the question is whether you can use the STL without throwing exceptions. Assuming that standard containers are part of "the STL", and you want to do things with them that might allocate memory, you have to write an allocator to use as a template parameter with that container, since the default allocator can throw. Yes, it could just use nothrow new, and if that fails I guess it would have to abort. Whatever you use, though, you have to write a memory allocator. That said, probably if the performance cost of an exception is unacceptable, so is the cost of memory allocation.
Steve Jessop
+2  A: 

It's worth noting a couple of points:

  • Your application is multi-threaded. If one thread (maybe a GUI one) is slowed down by an exception, it should not affect the performance of the real-time threads.

  • Exceptions are for exceptional circumstances. If an exception is thrown in your real-time thread, the chances are it will mean that you couldn't continue playing audio anyway. If you find for whatever reason that you are continually processing exceptions in those threads, redesign to avoid the exceptions in the first place.

I'd recommend you accept the STL with it's exceptions (unless the STL itself proves too slow - but remember: measure first, optimise second), and also to adopt exception handling for your own 'exceptional situations' (audio hardware failure, whatever) in your application.

Roddy
+6  A: 

It's not clearly written in the previous answers, so:

Exceptions happen in C++

Using the STL or not won't remove the RAII code that will free the objects's resources you allocated.

For example:

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

In the code above, the compiler will generate the code to free the MyString resources (i.e. will call the MyString destructor), no matter what happens in the meantime including if if an exception is thrown by doSomethingElse or if you do a "return" before the end of the function scope.

If you have a problem with that, then either you should revise your mindset, or try C.

Exceptions are supposed to be exceptional

Usually, when an exception occurs (and only when), you'll have a performance hit.

But then, the exception should only sent when:

  • You have an exceptional event to handle (i.e. some kind of error)
  • In very exceptional cases (i.e. a "massive return" from multiple function call in the stack, like when doing a complicated search, or unwinding the stack prior a thread graceful interruption)

The keyword here is "exceptional", which is good because we are discussing "exception" (see the pattern?).

In your case, if you have an exception thrown, chances are good something so bad happened your program would have crashed anyway without exception.

In this case, your problem is not dealing with the performance hit. It is to deal with a graceful handling of the error, or, at worse, graceful termination of your program (including a "Sorry" messagebox, saving unsaved data into a temporary file for later recovery, etc.).

This means (unless in very exceptional cases), don't use exceptions as "return data". Throw exceptions when something very bad happens. Catch an exception only if you know what to do with that. Avoid try/catching (unless you know how to handle the exception).

What about the STL ?

Now that we know that:

  • You still want to use C++
  • Your aim is not to throw thousand exceptions each and every seconds just for the fun of it

We should discuss STL:

STL will (if possible) usually verify if you're doing something wrong with it. And if you do, it will throw an exception. Still, in C++, you usually won't pay for something you won't use.

An example of that is the access to a vector data.

If you know you won't go out of bounds, then you should use the operator [].

If you know you won't verify the bounds, then you should use the method at().

Example 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 ;
    }
}

Example B:

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

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

The example A "trust" the programmer, and no time will be lost in verification (and thus, less chance of an exception thrown at that time if there is an error anyway... Which usually means the error/exception/crash will usually happen after, which won't help debugging and will let more data be corrupted).

The example B asks the vector to verify the index is correct, and throw an exception if not.

The choice is yours.

paercebal
I think BTW, that you should always default to using `at()`, and only afterwards optimize away the `at`s into `[]`. Many times the code is not performance criticial, and it's better safe than sorry.
Elazar Leibovich
@Elazar Leibovich : I'm using `[]` by default, but I guess you're right. There's no reason to not use `at()` instead, if but for the syntactic sugar...
paercebal