nothrow by default
Johannes Pfau
nospam at example.com
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:
>
> 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.
--
Johannes
More information about the Digitalmars-d
mailing list