Exception Handling

Tutorials

 

Enabling      

Performance Impact      

Improving Performance      

Tutorial Index 

 

Enabling Exceptions

By default, exception-handling is turned on. When exception-handling is turned off, exception-handling constructs such as throw, catch, and try are rejected and flagged by error messages. When exception-handling is turned on, KAI C++ accepts and compiles exception-handling constructs. The command-line option for turning off exception-handling is shown below.

        --no_exceptions

Exception-handling usually incurs a slight performance penalty, even if exception-handling constructs are not used. The reason is that whenever a call to a function is made, there is potential for an exception to be thrown "over the head" of the caller, which requires destroying all local objects. This implies that the control-flow of the program is inherently more complex. At the highest levels of optimization, the extra overhead is usually small. We measured it to be about 10% for a numerics-intensive benchmark.


Performance Impact

General point: Some implementations of C++ claim to have "zero overhead" exceptions. This is impossible as exceptions inherently add new paths of control flow to a program. What they really mean by "zero overhead" is "prepaid overhead". (I like the vendor who advertises "zero overhead", but has a switch to turn off exceptions to enable a "faster smaller executable".)

The way to find out what the overhead is for exception handling is to benchmark your own application with it turned on and off. In our experience, the overhead is usually in the 1-10% range.

If you have exceptions enabled, and your code does not use exceptions, and does not use classes that throw exceptions, there is still a performance penalty to be paid. Every local (auto) object that has a nontrivial destructor is added to an "exception stack" during normal execution. If over the local object's lifetime, there are no non-inline calls, the optimizer (usually) removes the push and pop of this object onto the "exception stack". This keeps the performance penalty for exception handling fairly low, especially in inner loops, where the optimizer is at its best.


Improving Performance

The one area in which some programmer intervention pays off is with regard to code bloat. Inline try blocks and inline throws can cause significant code bloat. We recommend that all try blocks and throw expressions be put out of line. For example, consider the following fragment:

    template<class T> class Array {
    public:
        T operator[]( int k ) {
            if( k>=my_size ) throw out_of_range("Array::operator[]");
            return my_array[k];
        }
    private:
        size_t my_size;
        T * my_array;
        ...
    };

The operator[] is implicitly inline because its definition is coded within the class. It is desirable here to make the normal execution path fast and small in terms of code size. But inlining the exceptional path causes code bloat, as every call site for operator[] will duplicate the code for throwing an exception. The solution is to partition the logic of operator[] into two parts: an inline part for the normal case, and an out-of-line part for the exceptional case. Further improvement can be gained by making the out-of-line part a non-template if possible, so that it is not replicated for each distinct T. Below is the repartitioned code:

    class ArrayBase {            // Not a template class
    protected:
        void index_error();      // Out of line
    };
    void ArrayBase::index_error() {
        throw out_of_range("Array::operator[]");
    }
    template<class T> class Array: private ArrayBase {
    public:
        T operator[]( int k ) {
           if( k>=my_size ) index_error();
           return my_array[k];
        }
    private:
        size_t my_size;
        T * my_array;
        ...
    };

This technique is used in the KAI C++ standard class library itself. Instead of directly invoking throw, it invokes an out-of-line static method __throw that does the work.

We recommend factoring out template-independent code from templates not just for exceptions, but for all code. Significant reductions in code size and compilation time can be accomplished. For example, KAI C++ version 3.2 has a refactored <map> class that compiles 5x faster and generates code 3x smaller than the previous (unfactored) one did.



Copyright © 1996-1999 by Kuck & Associates, Inc. All rights reserved.

E-Mail KAI Technical Support   E-Mail KAI   Contact KAI   

This file last updated on 4 June 1999.