January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Marc Schütz | On 1/13/2015 11:42 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote: > On Tuesday, 13 January 2015 at 00:05:31 UTC, Walter Bright wrote: >> On 1/12/2015 3:17 PM, deadalnix wrote: >>> On Monday, 12 January 2015 at 22:17:57 UTC, Walter Bright wrote: >>>> Yes, it does. Returning an int in EAX now becomes returning a pair [EAX,EDX]. >>> >>> It is not that big of a deal, EDX is a trash register anyway if memory serve, >>> but then, it become very bad when it do not fit in register anymore. >> >> Returning a slice, for example, already consumes EAX and EDX. Adding an error >> code pushes that into returning via a pointer to a stack temporary. >> > > There are techniques to mitigate that. For example, Rust is smart enough to > encode the "not-present" state for pointers as a NULL pointer (Rust pointers are > non-null). Such mitigation techniques pretty much confirms there's an efficiency cost to having the error code. > > If an error code needs to be returned, it can be encoded as a misaligned pointer > (if you're feeling adventurous). Don't know whether Rust does the latter, though. > > Of course, these things have costs of their own. |
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russel Winder | On 1/13/2015 3:01 AM, Russel Winder via Digitalmars-d wrote: > > On Mon, 2015-01-12 at 12:58 -0800, Walter Bright via Digitalmars-d wrote: >> On 1/12/2015 11:30 AM, Russel Winder via Digitalmars-d wrote: >>> Go has an interesting solution, key lookup in a map return a pair >>> (result, ok), if lookup succeeded then result is the associated >>> value, if ok is false then result is undefined. I quite like this. >> >> >> That's just putting the responsibility of array bounds checking on >> the caller. >> >> Would you want (result, ok) returned every time you indexed an >> array? I sure >> wouldn't. An associative array isn't conceptually any different. > > Why not? > > There is a fundamentally different approach to error handling > depending on whether exceptions are integral cf. Python or anathema > cf. Go and most functional programming languages. It is unreasonable > to impose the idioms of one model onto another. And vice versa. My point was more to the issue of how is array bounds checking done in those languages, and that aa checking should behave the same way when doing the same operations. > arrays, sparse arrays and associative arrays are both very different > and quite similar. The properties of the keys makes for very different > approaches to algorithms, and possibly error handling of key lookup: > contiguity of keys of an array is important. In trying to make component programming work, I look for emphasizing similarities between containers rather than differences, in this case, what happens when presenting an invalid index. Similarity in basic operations like that make it easy to swap in and out different containers without having to redo the rest of the code's interface to it. |
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | 13-Jan-2015 02:01, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang@gmail.com>" пишет: > On Monday, 12 January 2015 at 22:06:32 UTC, deadalnix wrote: >> If you put aside performance concerns, exceptions for control flow >> also tend to make many code path implicit and makes for very >> unreadable/unmaintainable code. > > But exceptions are control flow. There is no such thing as normalized > control flow, all state changes implies "control flow". Think in terms > of a state machine. You could just remove all variables and only have a > big state machine (assuming finite dimensions). Every single state > transition implies flow of control. > > The control flow you see in the source code is just the programmer's > "rendition" of control flow. Exceptions is a mechanism for getting > cluttering resolution out of that rendition to make the code more > readable and maintainable. The goal is to retain the meat of the > computation and remove the noise. > > Or to put it differently, there are many ways to write the same > function. Good use of exceptions removes the clutter and leaves the > things you care about visible. It's a mechanism, not a moral issue or a > religion. > > So there is nothing wrong with throwing HTTPStatus(409) or > HTTPStatus(201), even though it returns state to the environment. If > that means the code is more maintainable and more likely to be correct, > then that is good use of the mechanism. Actually I agree - exceptions are mechanism. Stressing _exceptional_ as in "happening very rarely" is just a poor excuse to never optimize this control-flow mechanism. I guess C/C++ inspired languages would never have fast exceptions simply because of the mindset with the perception of them being non-important or rare. Big adopters of C++ often avoided exceptions entirely to not pollute binaries with EH-code only making this issue worse. In contrast, Java optimizes exceptions and re-writes many try/catch to plain "gotos on error" control flows. -- Dmitry Olshansky |
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Marc Schütz | On Tuesday, 13 January 2015 at 19:36:31 UTC, Marc Schütz wrote:
> On Monday, 12 January 2015 at 23:01:53 UTC, Ola Fosheim Grøstad wrote:
>> On Monday, 12 January 2015 at 22:06:32 UTC, deadalnix wrote:
>>> No, Exception are a bail out mechanism. It is the, I have no idea what to do about this mechanism.
>>
>> The way it is done in C++, yes.
>>
>>> If you put aside performance concerns, exceptions for control flow also tend to make many code path implicit and makes for very unreadable/unmaintainable code.
>>
>> But exceptions are control flow. There is no such thing as normalized control flow, all state changes implies "control flow". Think in terms of a state machine. You could just remove all variables and only have a big state machine (assuming finite dimensions). Every single state transition implies flow of control.
>>
>> The control flow you see in the source code is just the programmer's "rendition" of control flow. Exceptions is a mechanism for getting cluttering resolution out of that rendition to make the code more readable and maintainable. The goal is to retain the meat of the computation and remove the noise.
>>
>> Or to put it differently, there are many ways to write the same function. Good use of exceptions removes the clutter and leaves the things you care about visible. It's a mechanism, not a moral issue or a religion.
>>
>> So there is nothing wrong with throwing HTTPStatus(409) or HTTPStatus(201), even though it returns state to the environment. If that means the code is more maintainable and more likely to be correct, then that is good use of the mechanism.
>
> I usually don't do this, but I really need to post a "+1" here:
>
> +1
Too bad you chose to do this on a bad strawmman.
|
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Tuesday, 13 January 2015 at 03:05:57 UTC, Walter Bright wrote: > On 1/12/2015 4:40 PM, deadalnix wrote: >> These are trash register. Meaning the callee can put whatever in them. The >> caller must consider them trashed after the call. >> >> So no, it do NOT increase register pressure. > > 1. the register must be assigned a value - that has a cost xor on Haswell has a reciprocal throughput of 0.25, meaning execution of 4 instructions per cycle per core. > 3. it prevents EDX from being used by the return value, when the return value is larger than a single register size Store the error in TLS memory using a write-through (no load from memory) instruction and use the carry-flag instead then (assuming the return instruction does not clear carry). CLC has reciprocal throughput of 0.25 too. > 4. static functions may not need to follow the C ABI register convention, and can be so optimized D calls D, no problem. D calls C, no problem, C can't return D errors... C calls D, a problem no matter what solution you pick. |
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | Of course the best solution might be to just implement reasonably fast exceptions by: 1. Restricting the exception object to 512 bytes, preallocated in TLS. 2. Only have one exception in flight per thread 3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight. 4. Leave it to the implementation how to propagate awareness of an exception. (LLVM has many different calling conventions, it might be desirable to do it different for each one). 5. Constrain calls from C to D to nothrow functions. |
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On Tuesday, 13 January 2015 at 22:46:26 UTC, Ola Fosheim Grøstad wrote: > Of course the best solution might be to just implement reasonably fast exceptions by: > > 1. Restricting the exception object to 512 bytes, preallocated in TLS. > Exception can bubble from one thread to another. > 2. Only have one exception in flight per thread > I don't see how you could have more. > 3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight. > The first 8 bytes are already what is used to do matching. But, because of inheritance, it is not as simple as you think it is. Also, who the fuck care what is in the buffer when no exception are in flight ? Skipping 4 and 5, you obviously didn't though that through. |
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Tuesday, 13 January 2015 at 23:40:41 UTC, deadalnix wrote: >> 1. Restricting the exception object to 512 bytes, preallocated in TLS. >> > > Exception can bubble from one thread to another. How? If you are thinking coroutines, then the exception can be moved with it. >> 2. Only have one exception in flight per thread >> > > I don't see how you could have more. In a catch you can throw a new one and initialize with the old, but if they are both in the same memory space it will go wrong. > >> 3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight. >> > > The first 8 bytes are already what is used to do matching. But, because of inheritance, it is not as simple as you think it is. Also, who the fuck care what is in the buffer when no exception are in flight ? 1. So that you can mark a non-D function with @checkexception and allow it to both cast D-exceptions and propagate D-exceptions. > Skipping 4 and 5, you obviously didn't though that through. ? |
January 13, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On Tuesday, 13 January 2015 at 23:47:58 UTC, Ola Fosheim Grøstad wrote: > On Tuesday, 13 January 2015 at 23:40:41 UTC, deadalnix wrote: >>> 1. Restricting the exception object to 512 bytes, preallocated in TLS. >>> >> >> Exception can bubble from one thread to another. > > How? If you are thinking coroutines, then the exception can be moved with it. > http://dlang.org/phobos/core_thread.html#.Thread.join >>> 2. Only have one exception in flight per thread >>> >> >> I don't see how you could have more. > > In a catch you can throw a new one and initialize with the old, but if they are both in the same memory space it will go wrong. > When you are in the catch, you are not unwinding anymore. You could indeed loose chaining to be able to reuse the memory, that is not what is slow when it come to exception, so it won't make it fast. >>> 3. Require that the first 8 bytes of the exception buffer are 0 when no exception is in flight, and use them for encoding exception type when one is in flight. >>> >> >> The first 8 bytes are already what is used to do matching. But, because of inheritance, it is not as simple as you think it is. Also, who the fuck care what is in the buffer when no exception are in flight ? > > 1. So that you can mark a non-D function with @checkexception and allow it to both cast D-exceptions and propagate D-exceptions. > Empty hand waving, not not addressing the point. >> Skipping 4 and 5, you obviously didn't though that through. > > ? What you propose in point 1 - 3 is weak at best. You haven't put serious effort thinking about what you are proposing, so there is no reason anyone should spend any serious effort criticizing it. |
January 14, 2015 Re: Why exceptions for error handling is so important | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Tuesday, 13 January 2015 at 23:58:53 UTC, deadalnix wrote: > > http://dlang.org/phobos/core_thread.html#.Thread.join I don't see the problem. I'm suggesting value semantics, it can be copied. > When you are in the catch, you are not unwinding anymore. You could indeed loose chaining to be able to reuse the memory, that is not what is slow when it come to exception, so it won't make it fast. You can't have chaining with this scheme...? But since you can only have one instance it has to be initialized with value semantics. It's a small constraint. Not using regular Itanium-style unwinding will make it potentially faster. Using TLS makes it possible to propagate over non-D code or code where the register pressure is high, it can be optimized away so you don't need TLS if you don't span over non-D code. >> 1. So that you can mark a non-D function with @checkexception and allow it to both cast D-exceptions and propagate D-exceptions. >> > > Empty hand waving, not not addressing the point. Who are you waving at? Non D functions can cast by setting the TLS memory to an exception value, then do a regular return. The D function that called the non-D function will check TLS memory to see if there is an exception in flight. >>> Skipping 4 and 5, you obviously didn't though that through. >> >> ? > > What you propose in point 1 - 3 is weak at best. You haven't put serious effort thinking about what you are proposing, so there is no reason anyone should spend any serious effort criticizing it. Then don't bother. If your point regarding point 4 was that it would limit cross compiler linking, then yes. And that would be intentional, since it is far too soon to standardize D at that level. It just prevents experimentation and performance gains. If your point regarding point 5 is that it would be too limiting, well then you would need to differentiate between D and C entrypoints to D functions to overcome the problem. That's possible, but IMO not worth it. |
Copyright © 1999-2021 by the D Language Foundation