February 07, 2014
Walter Bright <newshound2@digitalmars.com> writes:

> On 2/6/2014 6:19 PM, Andrei Alexandrescu wrote:
>> On 2/6/14, 5:23 PM, Walter Bright wrote:
>>> I'm tempted to say that the throw expression can call 'new' even if the function is marked as @nogc.
>>
>> That's extreme. A better possibility is to allocate exceptions from a different heap and proclaim that the heap is cleaned once all catch blocks are left. (I'm sure we can find something better, but now is not the time to worry about it.)
>
> That doesn't work, as nothing prevents code from squirreling away the caught exception object handle.

Very naive question (that may have already been answered), but why can't throw use structs instead of classes?  Then the exception would propagate by copy rather than passing the object up the stack?
February 07, 2014
On Friday, 7 February 2014 at 18:28:24 UTC, Jerry wrote:
> throw use structs instead of classes?

I think that'd be more costly and would mess up the whole inheritance checks; catch(Exception) wouldn't catch the same children.
February 07, 2014
Jerry:

>> throw use structs instead of classes?

This thread discusses the (low) performance of D exceptions, and suggests some ideas:
https://d.puremagic.com/issues/show_bug.cgi?id=9584

Another thread:
https://d.puremagic.com/issues/show_bug.cgi?id=9581

The thread also discusses an old idea from Java:
http://www.javaspecialists.eu/archive/Issue187.html

Bye,
bearophile
February 07, 2014
On Friday, 7 February 2014 at 17:06:36 UTC, Dmitry Olshansky
wrote:
> 07-Feb-2014 20:49, Sean Kelly пишет:
>> On Friday, 7 February 2014 at 16:41:00 UTC, Dmitry Olshansky wrote:
>>>
>>> Meh. If exceptions are such a liability we'd better make them (much)
>>> faster.
>>
>> It's not stack unwinding speed that's an issue here though, but rather
>> that for client-facing services, throwing an exception when an invalid
>> request is received gives malicious clients an opportunity to hurt
>> service performance by flooding it with invalid requests.
>
> Why throwing a single exception is such a big problem? Surely even C's long_jump wasn't that expensive? *Maybe* we shouldn't re-construct  full stack trace on every throw?

That can be turned off at run time by clearing the traceHandler.
But yeah, it's the allocations that are a problem in this case,
not the unwinding.  And specifically, that flooding with bad
requests effectively generates tons of garbage (an allocation for
the exception plus another for the trace data) thus triggering
frequent stop-the-world collections.


> Exceptions are convenient and they make life that much easier combined with ctors/dtors and scoped lifetime. And then we say **ck it - for busy services, just use good ol':
> ...
> if (check42(...) == -1){ call_cleanup42(); return -1; }
> ...
>
> And up the callstack we march. The moment code gets non-trivial there come exceptions and RAII to save the day, I don't see how busy REST services are unlike anything else.

I'm sure you can see how a service is different from a desktop
application, right?  In the latter case, there's only one user
and he's interested in having his application perform well.
Outside of a QA lab you won't find desktop app. users
deliberately trying to break their app.  Services are exactly the
opposite.  It's not an exaggeration when I say that the services
I work on are under attack from botnets 24/7.  This is a use case
that must be considered as a first order of business or the
entire service suffers.


>> I'm not convinced that there's any need for a language change here to support scoped exceptions.  That seems a bit like killing the ant with a steamroller.
>
> Well I'm not convinced we should accept that exceptions are many times slower then error codes (with checks on every function that may fail + propagating up the stack).

Exception-oriented code is typically faster for the success case
because all that return code checking can be removed.  But the
tradeoff is that it's slower in the failure case because stack
unwinding is simply slower than checking an error code.  But
again, the issue here isn't the cost of stack unwinding, it's
that thousands of exceptions thrown per second generates a lot of
garbage, and garbage collection in D is currently fairly slow
compared to, say, Java.  If we could get an incremental GC for D
I probably wouldn't even care, but I think that's impossible.
February 07, 2014
On Friday, 7 February 2014 at 18:45:24 UTC, bearophile wrote:
> Jerry:
>
>>> throw use structs instead of classes?
>
> This thread discusses the (low) performance of D exceptions, and suggests some ideas:
> https://d.puremagic.com/issues/show_bug.cgi?id=9584
>
> Another thread:
> https://d.puremagic.com/issues/show_bug.cgi?id=9581
>
> The thread also discusses an old idea from Java:
> http://www.javaspecialists.eu/archive/Issue187.html

Okay, I'm going to look into generating traces lazily.  I think
it should be possible.
February 07, 2014
On Fri, 07 Feb 2014 10:54:37 -0800, Sean Kelly <sean@invisibleduck.org> wrote:

> On Friday, 7 February 2014 at 17:06:36 UTC, Dmitry Olshansky
> wrote:
>> 07-Feb-2014 20:49, Sean Kelly пишет:
>>> On Friday, 7 February 2014 at 16:41:00 UTC, Dmitry Olshansky wrote:
>>>>
>>>> Meh. If exceptions are such a liability we'd better make them (much)
>>>> faster.
>>>
>>> It's not stack unwinding speed that's an issue here though, but rather
>>> that for client-facing services, throwing an exception when an invalid
>>> request is received gives malicious clients an opportunity to hurt
>>> service performance by flooding it with invalid requests.
>>
>> Why throwing a single exception is such a big problem? Surely even C's long_jump wasn't that expensive? *Maybe* we shouldn't re-construct  full stack trace on every throw?
>
> That can be turned off at run time by clearing the traceHandler.
> But yeah, it's the allocations that are a problem in this case,
> not the unwinding.  And specifically, that flooding with bad
> requests effectively generates tons of garbage (an allocation for
> the exception plus another for the trace data) thus triggering
> frequent stop-the-world collections.
>
>
>> Exceptions are convenient and they make life that much easier combined with ctors/dtors and scoped lifetime. And then we say **ck it - for busy services, just use good ol':
>> ...
>> if (check42(...) == -1){ call_cleanup42(); return -1; }
>> ...
>>
>> And up the callstack we march. The moment code gets non-trivial there come exceptions and RAII to save the day, I don't see how busy REST services are unlike anything else.
>
> I'm sure you can see how a service is different from a desktop
> application, right?  In the latter case, there's only one user
> and he's interested in having his application perform well.
> Outside of a QA lab you won't find desktop app. users
> deliberately trying to break their app.  Services are exactly the
> opposite.  It's not an exaggeration when I say that the services
> I work on are under attack from botnets 24/7.  This is a use case
> that must be considered as a first order of business or the
> entire service suffers.
>
>
>>> I'm not convinced that there's any need for a language change here to support scoped exceptions.  That seems a bit like killing the ant with a steamroller.
>>
>> Well I'm not convinced we should accept that exceptions are many times slower then error codes (with checks on every function that may fail + propagating up the stack).
>
> Exception-oriented code is typically faster for the success case
> because all that return code checking can be removed.  But the
> tradeoff is that it's slower in the failure case because stack
> unwinding is simply slower than checking an error code.  But
> again, the issue here isn't the cost of stack unwinding, it's
> that thousands of exceptions thrown per second generates a lot of
> garbage, and garbage collection in D is currently fairly slow
> compared to, say, Java.  If we could get an incremental GC for D
> I probably wouldn't even care, but I think that's impossible.

Technically, there is no reason that the current GC can't be made incremental, insofar as incremental means collecting only what is required complete the allocation.

-- 
Adam Wilson
GitHub/IRC: LightBender
Aurora Project Coordinator
February 07, 2014
On 2/7/2014 3:42 AM, Dicebot wrote:
> P.S. Throwing exception is not that slow in D, it is allocating new instance
> that makes a huge impact.

Throwing speed can vary greatly from platform to platform.

The idea, as in C++, is when there's a speed tradeoff between throw/catch speed and compromising speed to handle the possibility of exceptions, the non-throw case gets priority.
February 07, 2014
On 2/7/2014 7:33 AM, Adam D. Ruppe wrote:
> On Friday, 7 February 2014 at 11:37:16 UTC, Ola Fosheim Grøstad wrote:
>> How slow is slow? Is it slower than in Go and Python?
>
> One problem with allocating the exception is the stop-the-world thing. My
> cgi.d's built in httpd does some allocations in its constructor, which is run
> once per request. It can answer requests at a rate of about 6000/sec on my
> computer...

The gc is not the real speed issue with exceptions, after all, one can preallocate the exception:

    throw new Exception();

          v.s.

    e = new Exception();
    ...
    throw e;

It's the unwinding speed. Just have a look at what deh2.d has to do.

February 07, 2014
On Friday, February 07, 2014 20:40:57 Dmitry Olshansky wrote:
> 07-Feb-2014 06:44, Walter Bright пишет:
> > On 2/6/2014 2:15 PM, Brad Anderson wrote:
> >> Personally I don't think bad user input qualifies as an exceptional
> >> case because
> >> it's expected to happen and the program is expected to handle it (and
> >> let the
> >> user know) when it does. That's just a matter of taste though.
> > 
> > It's not a matter of taste. If your input is subject to a DoS attack, don't put exceptions in the control flow.
> 
> Meh. If exceptions are such a liability we'd better make them (much)
> faster.

Related: http://d.puremagic.com/issues/show_bug.cgi?id=9584

The DOS aspect of exceptions are not something that I've ever thought about or seen discussed before, but one area where I've found the slowness of D's exceptions to be a real pain is in unit tests. I like to test failure cases as well as successful ones, and if you do much of that, your unit tests start taking a long time due to how insanely slow exceptions are in D.

So, while in some situations, the solution may be to not use exceptions (or to use them less), I think that we really need to look at doing what's necessary to make exceptions a lot faster - be it to more efficiently deal with stack traces or to avoid allocating them or whatever else we can come up with to make them fast. I think that the approach of assuming that exceptions don't need to be fast, because they're used for error conditions is a bad one. They're not as performance critical as normal code, but their speed still very much matters.

- Jonathan M Davis
February 07, 2014
07-Feb-2014 22:54, Sean Kelly пишет:
> On Friday, 7 February 2014 at 17:06:36 UTC, Dmitry Olshansky
> wrote:
>>> It's not stack unwinding speed that's an issue here though, but rather
>>> that for client-facing services, throwing an exception when an invalid
>>> request is received gives malicious clients an opportunity to hurt
>>> service performance by flooding it with invalid requests.
>>
>> Why throwing a single exception is such a big problem? Surely even C's
>> long_jump wasn't that expensive? *Maybe* we shouldn't re-construct
>> full stack trace on every throw?
>
> That can be turned off at run time by clearing the traceHandler.

Which should be somehow prominently advertised for release builds. Last time I checked not making it null made exceptions ridiculously slow.

> But yeah, it's the allocations that are a problem in this case,
> not the unwinding.  And specifically, that flooding with bad
> requests effectively generates tons of garbage (an allocation for
> the exception plus another for the trace data) thus triggering
> frequent stop-the-world collections.

So again - the problem is allocations on GC heap. Then let's please not worry about tiny gains of avoiding stack unwind, that is well understood.

And I see no reason for allocating exceptions on GC (and none presented so far). The main use case of exception is to consume exception on catch or forward it down the line. Storing a reference to an exception elsewhere is rare case. I could see the whole situation with exceptions in D as
"we copied this shit from Java, no idea why"

Java at least does go to great lengths to make them fast
(by caching them behind the scenes and whatnot).

>
>> Exceptions are convenient and they make life that much easier combined
>> with ctors/dtors and scoped lifetime. And then we say **ck it - for
>> busy services, just use good ol':
>> ...
>> if (check42(...) == -1){ call_cleanup42(); return -1; }
>> ...
>>
>> And up the callstack we march. The moment code gets non-trivial there
>> come exceptions and RAII to save the day, I don't see how busy REST
>> services are unlike anything else.
>
> I'm sure you can see how a service is different from a desktop
> application, right?

Aye, in fact I haven't written much in the way of desktop apps.

> In the latter case, there's only one user
> and he's interested in having his application perform well.
> Outside of a QA lab you won't find desktop app. users
> deliberately trying to break their app.  Services are exactly the
> opposite.  It's not an exaggeration when I say that the services
> I work on are under attack from botnets 24/7.  This is a use case
> that must be considered as a first order of business or the
> entire service suffers.

I bet some sanity checks on the level of protocol handling is more then enough.
Yeah these might be faster then unwinding due to shear volume of bad data, but it's a fraction of code albeit a critical fraction.

I was thinking about the service logic on top of that.

>
>>> I'm not convinced that there's any need for a language change here to
>>> support scoped exceptions.  That seems a bit like killing the ant
>>> with a steamroller.
>>
>> Well I'm not convinced we should accept that exceptions are many times
>> slower then error codes (with checks on every function that may fail +
>> propagating up the stack).
>
> Exception-oriented code is typically faster for the success case
> because all that return code checking can be removed.  But the
> tradeoff is that it's slower in the failure case because stack
> unwinding is simply slower than checking an error code.

Duly noted. Just stating the obvious - in the majority of cases we talk about 1 unwind vs 10s of checks. The difference isn't THAT big anyway, the only advantage of codes checking is being able to fail faster on some _early_ bad condition.

> But
> again, the issue here isn't the cost of stack unwinding, it's
> that thousands of exceptions thrown per second generates a lot of
> garbage, and garbage collection in D is currently fairly slow
> compared to, say, Java.

Let's stop bashing GC here. This part of design of exceptions in D is just backwards (penalizes usual case) - time to fix it?



-- 
Dmitry Olshansky
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19