Live data from Hacker News

Avoid exception throwing in performance-sensitive code

lemire.me

161–170 of 243 posts

Re: Avoid exception throwing in performance-sensitive code

#161

Here's another link about the problems with C++ exception that I find very insightful https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p25... The major issue that stood out to me is that in a multi-threaded environment, exception unwinding is effectively single-threaded. This means that if you have C++ code that throws a lot of exceptions, you are going to see a lot of threads getting blocked by lock contenti…

to be clear, the single-threaded behaviour of some implementations is, well, implementation specific. There is nothing in the C++ standard that requires that. In particular, for GCC under linux, the issue is that the unwinder need to parse the unwind tables and needs to protect itself from a concurrent ldclose yanking them under it. There is some work currently on GCC to move this cost from unwinding to ldclose and a…

Sounds like a job for hazard pointers.

Re: Avoid exception throwing in performance-sensitive code

#162

I remember programming in Ada in my undergrad years and using exceptions for control flow was a common idiom. It was only later and in other languages that exceptions were treated purely as error conditions. Using exceptions allows you to separate corner cases from the normal logic. This was very clean -- not ugly as the author infers. His example is ugly, but is a straw man example.

It depends on how this is converted into machine level code. In quite a few cases it breaks down into rather ugly hacks, which is what creates the notion that they are "expensive" (and they are in these cases), and concluding from that should only be used sparingly, especially in code which is performance sensitive. With a sane ABI that includes exceptions as part of the daily life, so to speak, it is also possible to shift normal control flow to them without having any (larger) performance impact from it.

Re: Avoid exception throwing in performance-sensitive code

#163
post #161

Earlier quoted context omitted.

to be clear, the single-threaded behaviour of some implementations is, well, implementation specific. There is nothing in the C++ standard that requires that. In particular, for GCC under linux, the issue is that the unwinder need to parse the unwind tables and needs to protect itself from a concurrent ldclose yanking them under it. There is some work currently on GCC to move this cost from unwinding to ldclose and a…

Sounds like a job for hazard pointers.

Possibly! Alternatively a biased RW lock might work.

Re: Avoid exception throwing in performance-sensitive code

#164

Earlier quoted context omitted.

"Exceptions should be... the exception." :)

The problem is exceptions have entirely opaque flow control. They're the opposite of a goto statement: a comes-from statement if you will. Flow control could originate literally anywhere down the stack and that makes reasoning about what's happening very difficult. Depending on the language it can also have a super broad and ever-changing surface area.

Unchecked exceptions need extra care. I've seen process level exceptions being logged when the underlying cause was a failure to parse an integer. The exception was being caught in an exception handler half a dozen scopes away.

Re: Avoid exception throwing in performance-sensitive code

#165

Earlier quoted context omitted.

But there are a bunch of codes which are yet to be verified as exception-safe. And in the gaming industry it's often third party code that you cannot even inspect.

The right statement is then that it is very hard to retrofit exceptions in an non-exception safe code base. Exception safety itself is not easy, as it requires different code patterns and idioms than usual. I believe that those idioms are useful and great even for non-exceptional code, but that's another story.

I agree and would replace my original statment of "It's very hard to write exception safe code in C++" with this.

Re: Avoid exception throwing in performance-sensitive code

#166

Earlier quoted context omitted.

Exception is a way to return from a function abnormally. So yes, they are for exceptional conditions. IMO it's perfectly fine to use them for input validation. The Java standard library itself does it all the time.

Java is the... uhm... exception here. That's not how idiomatic C++ is. Nor Rust. Nor Go. One of Java's (incl standard library) main design flaws is overuse of exceptions. It should not be emulated. It's too late to fix Java, but that doesn't make it a good idea.

So what do you do in C++ if the input to your function is not what you expect?

Re: Avoid exception throwing in performance-sensitive code

#167
post #43

Exceptions are starting to feel like a legacy programming paradigm to me. Rust & Go have, at least in a practical programming context, shown that errors-as-values has far less footguns and encourages better error handling practices than exceptions, which often are treated as an afterthought or end up being abused like in this post. Whenever I'm writing Python or Java I can't help but feel anxious about calling a func…

> Exceptions are starting to feel like a legacy programming paradigm to me. Rust & Go have, at least in a practical programming context, shown that errors-as-values has far less footguns They don't really. Because they force you to handle errors even in code that couldn't care less about errors because that's the responsibility of a higher level up the stack. And worse, they assume that an exception is a valid reason…

Just remembered that Rust realised that manual handling of errors everywhere doesn't work, and introduced `try!` macro, and then the `?` operator.

These are more practical (and good thing Rust isn't rigid at the expense of pragmatism).

And of course since panics are not the answer to everything exceptional, there's now a `catch_unwind`, too

Re: Avoid exception throwing in performance-sensitive code

#168
post #65

Earlier quoted context omitted.

I think what Java got wrong was allowing catching unchecked exceptions. If Java had only allowed recovering from checked exceptions, it would have been a very similar experience to Rust's Result and panic. Instead, the trend was to avoid using checked exceptions at all, which perpetuated the crazy situation we're in where library code can suddenly abort and the author shrugs and says "shoulda read the docs".

> I think what Java got wrong was allowing catching unchecked exceptions. If Java had only allowed recovering from checked exceptions, it would have been a very similar experience to Rust's Result and panic. > Instead, the trend was to avoid using checked exceptions at all Java's type system was far too weak for that to be at all practical. Early Java not only didn't have first-class functions, it didn't even have ge…

> it's simply not possible to write a wrapper function that accepts a function and calls it, and throws the same set of checked exceptions that the inner function does

It is possible, and straightforward, to do that if the set of checked exceptions is of a statically known size, and the wrapper function can propagate the exceptions rather than catch and rethrow them. In my experience, that is a very common situation (although not universal).

If you want to catch and rethrow, that can also be done, but it requires an evil hack, and the wrapper will need to take a reified type for the exception.

Re: Avoid exception throwing in performance-sensitive code

#169

Earlier quoted context omitted.

Rust makes a distinction between recoverable and unrecoverable errors. Recoverable errors are the E in Result . You can take action and recover, depending on what kind of E it is. Unrecoverable errors are things like stack overflows or out of bounds array access. There is no reasonable way to soldier on after this, so the program should just end. Trying to continue the program in such situations only leads to pain. L…

> Unrecoverable errors are things like stack overflows or out of bounds array access. There is no reasonable way to soldier on after this, so the program should just end No, I wholeheartedly disagree with this. It's the equivalent of exit(1) some way down the stack. Whats recoverable or not depends on the use case and is a decision to be made by the caller of a function, not the implementor.

GP might have been referring to undefined/invalid behaviour (whether in the language or in some OS syscall or whatever). After the demons came out of your nose you can never fix the problem, so there is no point trying to handle the error.

Otherwise I agree with you, that library code should not fail/crash/exit(1) just because of some judgement about recoverability, and out to clean up after itself before passing control back to the caller. If the user wants to fix some ENOSPC deep in my library by shelling out to "rm -rf /" and then trying again, that's fine by me, and this should be reflected in the API.

Re: Avoid exception throwing in performance-sensitive code

#170
post #159

Earlier quoted context omitted.

At least for Go (haven't used Rust), I entirely disagree. After ~3 years of professional programming with the language, `if err != nil` is still constantly annoying me when writing code, but especially and most importantly, when reviewing code. Not to mention, Go has proven conclusively to me that exceptions are exactly the right pattern for error propagation - the 99.9% pattern is "function returns error with messag…

Absolutely. Even after "compressing" my code as much as possible, it looks as if 2/3 of a Go program is pure error handling and no business logic. I just don't understand this militant objection toward exceptions now. Even back in the days of slower virtual machines and crappier hardware, we were fine , and now it's code red! Get rid of exceptions! Performance! This is in an era of tech where everyone is building BAD…

>it looks as if 2/3 of a Go program is pure error handling and no business logic.

This is exactly why it's good. It forces developers to think about things outside the happy path business logic. And it does this at the point where they know best what such an error could mean.

Proponents of exceptions let their users catch the exceptions they overlooked.

If you ask me Go is not forcing this enough, the ML style Result types in Rust are a much better abstraction here.

Post reply on HN