nothrow by default

Johannes Pfau nospam at
Sun Jan 5 15:02:59 UTC 2020

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:
> 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:

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.


More information about the Digitalmars-d mailing list