Performance of exception handling

Sean Kelly sean at invisibleduck.org
Tue Apr 26 13:28:50 PDT 2011


On Apr 26, 2011, at 11:56 AM, Denis Koroskin wrote:

> On Tue, 26 Apr 2011 22:26:00 +0400, Sean Kelly <sean at invisibleduck.org> wrote:
> 
>> On Apr 26, 2011, at 9:29 AM, Denis Koroskin wrote:
>> 
>>> On Tue, 26 Apr 2011 20:14:05 +0400, Sean Kelly <sean at invisibleduck.org> wrote:
>>> 
>>>> Right now, traces are generated on throw. It should be possible to generate them on catch instead. The performance would be the same either way however. It would be nice to generate them lazily but I don't think that's possible.
>>>> 
>>>> Sent from my iPhone
>>>> 
>>> 
>>> Interesting enough, it is already done lazily (in toString(), which I believe should also cache result when it's called first time), but it can be improved a bit.
>> 
>> The readable version is generated in toString, but the actual trace occurs on throw.  This pretty much requires a memory allocation to store the trace info, which is one cause for the performance hit.
>> 
>> Originally, I didn't have the tracing enabled by default, but no one seemed to like that.  I could try enabling it only for non-release builds if that would be preferable.
> 
> You might have misunderstood me, or I might be wrong, but I believe you only need to store backtrace at the point where exception occurs, and resolve the symbols later (e.g. at toString()). Both are currently done in the DefaultTraceInfo ctor (see the code that you snipped).
> 
> Collecting the backtrace is very fast, and doesn't allocate on its own:
> 
> static enum MAXFRAMES = 128;
> void*[MAXFRAMES]  callstack;
> 
> numframes = backtrace( callstack, MAXFRAMES );
> 
> It's the "backtrace_symbols" that does all the hard work, I believe, and as such moving it out of the DefaultTraceInfo ctor should be enough.

It would make the DefaultTraceInfo class substantially larger, but that's a worthwhile tradeoff if performance is sufficiently improved.  I'll try it out.


> On the other had, DefaultTraceInfo could be made a struct and be moved to the Exception class to minimize allocations if you feel that exceptions should allocate as little as possible (if any at all, because they may end up throwing OutOfMemory exception).
> 
> Side question: we don't create OutOfMemory instances, throwing predefined ones instead, right? I don't think that work with exception chaining, does it? I mean, what if we e.g. catch an OOM, try to add a log entry, try to allocate and end up with OOM again. An exception chaining implemented with an intrusive linked list (i.e. implemented with an embedded Throwable next;) will not work for this case. Does druntime handle this case correctly?

The general rule for this is that if a static instance is deemed to be collateral then it's discarded, and if it should replace the current in-flight exception then the chain is lost.  This case used to be handled explicitly.  It isn't any more, but the code still appears to work (I tested to make sure a few weeks ago).  I want to look at this more carefully though, because I expected to see a segfault and was surprised when it didn't happen.  I don't like unexplained things.


More information about the Digitalmars-d mailing list