www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - nothrow by default

reply Kayomn <kayomn kayomn.net> writes:
https://forum.dlang.org/post/qustr2$1vnt$1 digitalmars.com

On Sunday, 5 January 2020 at 15:02:59 UTC, Johannes Pfau wrote:
 Am Sun, 05 Jan 2020 12:27:11 +0000 schrieb Gregor M├╝ckl:

 Consulting Agner Fox's microarchitecture manual, current Intel 
 CPUs still take a performance hit when encountering code with 
 a high density of conditional branches. They are sensitive to 
 the number of branch instructions per cache line. I've seen 
 GCC pad code with NOPs for that reason. AMD's branch 
 predictor, on the other hand, is described as perceptron that 
 fares better with repeated patterns of branches and isn't tied 
 to the cache. Branch mispredictions still hurt a lot (~20 
 cycles). All microcontrollers I'm familiar stall their 
 pipeline on every branch if they're pipelined at all.
 
 Moving error handling overhead back into the common/fast code 
 path is a step backwards in those cases where performance 
 matters. Desktops and servers can absolutely afford to have 
 the data overhead associated with exceptions and they benefit 
 the most from out of band error handling.
 
 It would be nice to have choice here.
This certainly needs to be considered, however: Some languages such as go (server!) and C++ with std:error decided to go for error codes for error handling. Here the proposal is more efficient and much more safe than completely manual error code handling. Also exceptions can be up to 1000 times slower for the error path. So you can now start doing statistics how exceptional your error path has to be for exception handling to be more effective. And if the error path is taken very rarely, it is easy for the branch predictor to predict. I have to admit, if you get rid of allocation and all the other exception complexity, the overhead of pure backtracing is probably less. But it's still significantly more than a simple branch. Regarding microcontrollers, how many microcontroller projects use full backtracing implementations? Every codebase I've ever seen for these systems uses error codes anyway. So Sutter's proposal would not affect the performance of these systems at all. The point about increased branch instruction density hurting the success path might still apply. But it's highly dependent on the relative amount of functions which actually use error handling. So it seems difficult to assess the impact without benchmarking an implementation.
 I hope that this doesn't require code to have a throw keyword 
 in every other line. Imagine outer functions of some algorithm 
 where the inner functions have a lot of opportunity to fail:
 
 auto result1 = throw step1();
 auto result2 = throw step2(result1);
 // etc...
 
 The value of the keyword decreases rapidly with the number of 
 occurrences.
That's certainly true. I think we need some kind of statistics on the distribution of nothrow / throw functions in D code here. This would also be useful information to make a more informed decision about the throw/ nothrow defaults. As long as calling throwing functions is uncommon, making the call explicit should be beneficial.
 There's recently been a talk about error types in C++ and 
 their evolution:
 
 https://www.youtube.com/watch?v=coBz_CQ1tJ8&
 
 As with everything in C++, there's a lot of complexity.
Thanks for the link, I'll have a look at this.
 
 Caveats:
 * I have not thought about how exception chaining fits into 
 all this.
This would require an exception to be allocated that starts the chain. The error return value could then turn into a pointer to that value. That's similar to how the C++ proposals return exceptions in error value returning functions.
This is actually similar to the way exception chaining is implemented in GDC right now. So that's probably not a hard problem.
 
 * These exceptions do not naturally propagate through foreign 
 language
   interfaces, although I think we don't guarantee this in D
 right now
   either.
D exceptions and C++ exception implementations are currently quite incompatible, but the documentation still states that that is an eventual goal of D: https://dlang.org/spec/cpp_interface.html#exception-handling
I was actually more thinking of a call chain like this: dfun1 => cppfun => dfun2. If dfun2 throws an exception, you can actually catch the exception in dfun1 (even though you do not know if cppfun properly ran its destructors). However, if dfun2 returns an error code union + flag, this error information does not propagate to dfun1.
Long time lurker of the forum but never really posted in any of the discussion topics, so I suppose I should first of all make clear how I use D. I only ever use the language in betterC mode with a library that I've hacked on top of the C standard library. I'm mentioning this to help better contextualize my views on things like exceptions, because fundamentally the version of D I use is a very different experience to what the majority probably have. Demonstrations of the safety and lower cost that result types bring, especially compiler-aware ones, have been seen recently in the up-and-comer Zig programming language. https://ziglang.org/documentation/master/#Error-Union-Type I quite like them as an alternative to exceptions due to how they force the user to deal with errors explicitly, creating less erroneous state in the program, similar to how `final switch` makes users account for all states in an enum in D. I currently roll my own result type, which is basically a templated struct that accepts the ok value type as its first type parameter and one or more error enum structures as the variadic and final type parameter. Internally, it creates a new enum from the provided enum members. From there I check if the result contains a success or error value according to a boolean flag. Zig takes this model a bit further than my approach by making result unions part of the language. If a function that can error "throws" inside its execution it just returns early with an error inside the return value. It uses a similar try catch syntax to exceptions, only it rigidly enforces the checking of anything that can error and makes sure all error states that the function can return are handled. Interested to read other's thoughts on this form of more compile-time focused error handling, as it seems like a far less costly alternative to exceptions in my estimation
Jun 30
parent Jacob Carlborg <doob me.com> writes:
On 2020-06-30 21:55, Kayomn wrote:

 Interested to read other's thoughts on this form of more compile-time 
 focused error handling, as it seems like a far less costly alternative 
 to exceptions in my estimation
I would like to have something like the zero overhead exceptions C++ proposal [1]. Then all the existing syntax used for exceptions can be reused. But with the performance of returning a result type. [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf -- /Jacob Carlborg
Jul 01