nothrow by default

Kayomn kayomn at kayomn.net
Tue Jun 30 19:55:36 UTC 2020


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


More information about the Digitalmars-d mailing list