February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Friday, 7 February 2014 at 19:54:14 UTC, Jonathan M Davis wrote:
> They're not as performance critical as normal code, but their speed still very much matters.
Well, it is at least more difficult to write reliable code when you have to try to avoid them. Still for a webservice you should probably not have to deal with more than 1000 per second on average, assume 1Ghz, then that is like 1.000.000 cycles of running code per stack unwinding.
If you sacrifice 10% of that for exception handling that means you have 100.000 cycles to unwind the stack. If the unwound stack is 5 frames deep you have 20.000 cycles per stack frame. If that is not possible something should be done with the Release-version of the runtime.
For a webserver you could of course tie the request handler directly to the request object and instantiate different ones for each request type then have all "unwinding" in the object itself. Quirky, but workable.
|
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | 07-Feb-2014 21:07, Andrej Mitrovic пишет: > On 2/7/14, Dmitry Olshansky <dmitry.olsh@gmail.com> wrote: >> Much simpler - it returns a special dchar to designate bad encoding. And >> there is one defined by Unicode spec. > > A NaN for chars? Sounds great to me! :) > It's called \uFFFD and is specifically for bad encodings. I wonder why nobody had perused the spec when writing std.utf.decode in the first place... 5.22 Best Practice for U+FFFD Substitution When converting text from one character encoding to another, a conversion algorithm may encounter unconvertible code units. This is most commonly caused by some sort of corruption of the source data, so that it does not correctly follow the specification for that character encoding. Examples include dropping a byte in a multibyte encoding such as Shift-JIS, improper concatenation of strings, a mismatch between an encoding declaration and actual encoding of text, use of non-shortest form for UTF-8, and so on. ... Whenever an unconvertible offset is reached during conversion of a code unit sequence: 1. The maximal subpart at that offset should be replaced by a single U+FFFD. 2. The conversion should proceed at the offset immediately after the maximal subpart. --- Fast, simple and according to the standard. Best of all - no stinkin' exceptions! ;) -- Dmitry Olshansky |
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On 2014-02-07 04:19, Sean Kelly wrote: > I really like vibe.d. A lot. But the way HTTP parse errors are handled > is a disaster. Do you know what happened when I was testing vibe.d > recently and I sent it a bad request? It sent a stack trace as a > responses. A stack trace! To a client! I was speechless. Needless to > say, I don't support the idea of further enabling this design, > regardless of whether it can be made a pinnacle of elegance. Ruby on Rails renders a page with a stack trace in development mode and a standard 500 page in production mode. I can't understand how anyone can do web development without that. There's even a plugin that renders a the stack trace as links pointing back to your editor (if supported). It also allows you to navigate the stack trace with a code snippet and simple debugger for each stack frame. Very convenient. -- /Jacob Carlborg |
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Marc Schütz | On Friday, February 07, 2014 14:26:47 Marc Schütz" <schuetzm@gmx.net>@puremagic.com wrote: > On Thursday, 6 February 2014 at 22:15:11 UTC, 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. > > Hmm... then what _does_ qualify as exceptional in your opinion? Honestly, I think that the typical approach of discussing exceptions as being for "exceptional" circumstances is bad. It inevitably leads to confusion and debate over what "exceptional" means. Some programmers would consider that to mean any bad input, whereas others would take it to the extreme that they should only happen when your program is in an invalid state (essentially what we use Errors for). I've found rather that when discussing exceptions it works much better to explain exactly why you'd use them, and I think that that comes primarily down to three types of circumstances. 1. Code which which should succeed most of the time and which would be far cleaner if it's written to throw exceptions - particularly when the alternative would be to check error codes on every function call (which would be incredibly error-prone). A prime example of this would be a parser. It's far cleaner to write a parser which assumes that each step succeeds than it is to constantly check that each one succeeded. It makes it so that only code that could actually encounter an error has to check for it and so that it can easily and cleanly propagate the error to the top. Doing that with error codes would generally be a mess, and unless failure is the norm, efficiency shouldn't be a problem. 2. Code which you can't actually guarantee will ever succeed. There are some cases where you can avoid errors by doing validation before proceeding (e.g. testing strings for Unicode correctness before doing a lot of string processing), but there are others where you either can't validate ahead of time or where you could still end up with an error in spite of your validation. A prime example of this would be operating on files. For, instance, std.file.isDir will tell you whether a particular file is directory or not by returning bool. If that file does not actually exist, then what is isDir supposed to do? All it can do is throw an exception, unless you want to have a separate out parameter to report whether it succeeded or not or change it so that it returns an error code and returns the bool as out parameter, both of which would make it much uglier to use. And isDir can't assert that the file exists, because that's a runtime condition that cannot be fully verified ahead of time. You can (and should) check whether the file exists first if(file.exists) { if(file.isDir) {} else if(file.isFile) {} else {} } but the file system could actually delete that file right out from under you between the call to exists and the call to isDir (or between the calls to isDir and isFile), so validation reduces how often you hit the error case but cannot eliminate it. It should also be rare that isDir will fail (since you should be checking that the file exists first). So, throwing an exception makes perfect sense. You get clean code that's still able to handle error cases rather than them being ignored (as frequently happens with error codes). 3. Code which should succeed most of the time but where doing validation essentially requires doing what you're validating for anyway. Again, parsers are a good example of this. For instance, to validate that "2013-12-22T01:22:27z" is in the valid ISO extended string format for a timestamp, you have to do pretty much exactly the same work that you have to do to parse out all of the values to convert it to something other than a string (e.g. SysTime). So, if you validated it first, you'd be doing the work twice. As such, why validate first? Just have it throw an exception when the parsing fails. And if for some reason, you expect that there's a high chance that the parsing would fail, then you can have a function which returns an error code and passed out the result as an out parameter instead, but that makes the code much uglier and error-prone. So, in most cases, you'd want it to throw an exception on failure. But regardless, you wouldn't want to validate it first as that would just be expensive all the time rather than more expensive in the (hopefully) rare error case. The areas that you want to normally avoid exceptions are when you're validating up front or when the error condition is likely. If you're validating, you're normally asking a question - is this data valid - in which case, returning bool is the correct thing to do, not throwing on failure (though if the result is false, the caller could choose to throw if appropriate). And trying to do something which has a good chance of failing should probably return whether it succeeded or not, because you don't want exceptions to be your normal code path. Also, performance-critical stuff may need to go the error-code path rather than exceptions simply due to it being performance-critical, but in general, error conditions which aren't bugs in your program should be reported via exceptions (not error codes) with validation being used where appropriate to make it so that the error conditions are infrequent. - Jonathan M Davis |
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On Friday, 7 February 2014 at 17:10:15 UTC, Ola Fosheim Grøstad wrote: > Ok, well I guess that primarily is an issue for validation errors where you need to return detailed error reporting. "Not Found" etc can be preallocated as immutable, or? yeah, preallocating exceptions might be a really good idea. > That sounds pretty good, was that as localhost, or over a network? localhost, and it was just hello world, performance of my thing degrades kinda quickly - it never gets /bad/, but it isn't great either once it starts doing more stuff than the basisc (but it is soooo easy to use! for me anyway) > You could synchronize them by calling the GC explicitly N seconds after the other process GC or you if you use a load balancer, maybe the GC could be scheduled by the load balancer or notify the load balancer (assuming all requests are short-lived). yeah. I'm not even sure if it would be a big deal in practice because there's often a lull anyway where the gc can get caught up (certainly not a problem for the lower traffic sites I mostly work on) |
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 07-Feb-2014 23:45, Walter Bright пишет: > 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; > And the standard library basically can't do this for every function. > It's the unwinding speed. Just have a look at what deh2.d has to do. It's deh.d or rather deh_win32./ deh_win64_posix.d and it doesn't look like _all_ that lot especially if you have no finally blocks and the only catch is the top-most catch-all. After all error codes would also have to propagate up the same call stack depth. -- Dmitry Olshansky |
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Friday, 7 February 2014 at 20:31:00 UTC, Jacob Carlborg wrote:
> On 2014-02-07 04:19, Sean Kelly wrote:
>
>> I really like vibe.d. A lot. But the way HTTP parse errors are handled
>> is a disaster. Do you know what happened when I was testing vibe.d
>> recently and I sent it a bad request? It sent a stack trace as a
>> responses. A stack trace! To a client! I was speechless. Needless to
>> say, I don't support the idea of further enabling this design,
>> regardless of whether it can be made a pinnacle of elegance.
>
> Ruby on Rails renders a page with a stack trace in development mode and a standard 500 page in production mode. I can't understand how anyone can do web development without that. There's even a plugin that renders a the stack trace as links pointing back to your editor (if supported). It also allows you to navigate the stack trace with a code snippet and simple debugger for each stack frame. Very convenient.
I was mostly surprised that the stack trace was written back to
the client. I'd expect something like that in a log on the
server side. I do see how it would be convenient to have a stack
trace included in a bug report, but if this feature is disabled
in release mode then you can't rely on it anyway. I'd just
always be checking the logs (where I'd hope the stack trace would
always be written).
|
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On Friday, 7 February 2014 at 20:41:01 UTC, Adam D. Ruppe wrote:
> yeah, preallocating exceptions might be a really good idea.
I wonder if it would be possible to get better unwinding speed by only throwing a single type of exception class and only a single catch. Then do pattern matching on an embedded typefield.
I.e.:
if (e.id & MASK_5xx) {}
if (e.id & MASK_409) {}
etc.
After looking at the code for stack unwinding it seems like keeping the loops short is essential.
|
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | Jonathan M Davis:
> 3. Code which should succeed most of the time but where doing validation
> essentially requires doing what you're validating for anyway. Again, parsers
> are a good example of this. For instance, to validate that
> "2013-12-22T01:22:27z" is in the valid ISO extended string format for a
> timestamp, you have to do pretty much exactly the same work that you have to
> do to parse out all of the values to convert it to something other than a
> string (e.g. SysTime). So, if you validated it first, you'd be doing the work
> twice. As such, why validate first? Just have it throw an exception when the
> parsing fails. And if for some reason, you expect that there's a high chance
> that the parsing would fail, then you can have a function which returns an
> error code and passed out the result as an out parameter instead, but that
> makes the code much uglier and error-prone. So, in most cases, you'd want it
> to throw an exception on failure.
Languages with a good type system solve this with Maybe / Nullable / Optional and similar things. It's both safe (and efficient if the result is equivalent to just a wapping struct).
Bye,
bearophile
|
February 07, 2014 Re: List of Phobos functions that allocate memory? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | On Friday, February 07, 2014 20:43:38 Dmitry Olshansky wrote:
> 07-Feb-2014 20:29, Andrej Mitrovic пишет:
> > On Friday, 7 February 2014 at 16:27:35 UTC, Andrei Alexandrescu wrote:
> >> Add a bugzilla and let's define isValid that returns bool!
> >
> > Add std.utf.decode() to that as well. IOW, it should have an overload
> > which returns a status code
>
> Much simpler - it returns a special dchar to designate bad encoding. And there is one defined by Unicode spec.
Isn't that actually worse? Unless you're suggesting that we stop throwing on decode errors, then functions like std.array.front will have to check the result on every call to see whether it was valid or not and thus whether they should throw, which would mean extra overhead over simply having decode throw on decode errors. validate has no business throwing, and we definitely should add isValidUnicode (or isValid or whatever you want to call it) for validation purposes. Code can then call that to validate that a string is valid and not worry about any UTFExceptions being thrown as long as it doesn't manipulate the string in a way that could result in its Unicode becoming invalid. However, I would argue that assuming that everyone is going to validate their strings and that pretty much all string-related functions shouldn't ever have to worry about invalid Unicode is just begging for subtle bugs all over the place IMHO. You're essentially dealing with error codes at that point, and I think that experience has shown quite clearly that error codes are generally a bad way to go. Almost no one checks them unless they have to. I think that having decode throw on invalid Unicode is exactly what it should be doing. The problem is that validate shouldn't.
- Jonathan M Davis
|
Copyright © 1999-2021 by the D Language Foundation