Live data from Hacker News

Avoid exception throwing in performance-sensitive code

lemire.me

201–210 of 243 posts

Re: Avoid exception throwing in performance-sensitive code

#201

Earlier quoted context omitted.

Return an error or fatally exit the program, depending.

If you return an error, it needs to be checked for in every place where this function is called. Yes, I know C libraries and OS APIs do this often, but that's C, it's the only thing it can do. This just invites human error. Besides, it's often desirable to handle multiple different error conditions (arising from different steps as you process the input data) in one place, which is complicated with this approach. Java…

This is fine, this is exception handling which you are describing. What exceptions should not be for is handling normal control flow. That is what branches are for. That is what the article is about and what people replying are complaining about.

Re: Avoid exception throwing in performance-sensitive code

#202

Earlier quoted context omitted.

I think conflating these two into one paradigm is worse. The catch-all (exceptions) style is nice only for a few very specific cases, like the request handler example. Everywhere else I want to either bubble up (like exceptions, but Result and ? sugar is as good or better), OR I want to handle the error. For the latter case, exceptions are not good at all.

IMHO, quite often when you're tempted to handle an error, you're either wrong to do so, or in some kind of infrastructure glue code. A request handler, a task executor, a strategy chain, a retry loop, you name it. And this code needs to deal with both classes of errors anyway to be bugfree.

From those examples, I think only request and task should deal with panics. Things that start threads or processes.

Other points of "catch all errors" don't need that. And then there are a lot of places where you do handle errors, if they are conceptually a Result. I know you can just catch SpecificError, but the ergonomics are just horrible in terms of control flow.

Re: Avoid exception throwing in performance-sensitive code

#203
post #8

Different languages have different exception handing optimizations. A Java version of the example can run very slow or very fast, depending on how clever you are. When a new RuntimeException is thrown half the time, the example runs about 650 times slower when compared to a function which adds up the integers without using exceptions. If I define an exception subclass which doesn't fill in the stack trace, then it ru…

I must admit I use exceptions heavily for validation, e.g. checking input at API bounds. It makes the code fairly clean. I would imagine this is considered bad practice, but if there was no overhead this seems preferable over wrapping every call in some wrapper object. Any good links to further info on this?

I hate to say it but you are using goto.

Goto is used often in C parsers for a reason, because it is easier to reason that way when you're validating something rather than using deeply nested branches. People shit on goto because of dijkstra's paper but few people have even read that paper and fewer even know that it is the origin of "x considered harmful" meme.

Goto is a boon. Anytime people use exceptions like how you are you're just admitting you're itching for a goto and your language doesn't provide it. And thus you shouldn't clutch your pearls when others use it in c.

Re: Avoid exception throwing in performance-sensitive code

#204
post #160

Earlier quoted context omitted.

Exceptions are typically custom types - you can build an arbitrarily detailed exception taxonomy (typically branching off the language-standard one), and in most (all?) languages supporting exceptions, you can also give them arbitrary state. You really can't get more specific than that. If exceptions still feel "not specific enough for handling errors", perhaps it's because you're only thinking of most trivial exampl…

You can build a separate exception for every possible raise site. But that is overly burdensome. Moreover, there is no guarantee that any (standard) library calls have an appropriately detailed taxonomy. You could, of course, wrap these library calls and catch exceptions so you can re-raise them yourself. But at that point you are losing the main advantage of exceptions over return values: the ability to separate the…

> You can build a separate exception for every possible raise site.

Why would you want to do that? You don't do that with Result/Either.

> To me, once I am building my own expansive taxonomy of exceptions, I am much happier using Optional, or Result/Either type return values.

I may be missing something, because to me, this doesn't follow. Optional type is "result or nothing"; and with Result/Either type, you either use something generic (e.g. symbol, string), or go very specific (even if it's just one of the dozen different "newtype" names for symbol/string). To me, this choice with Result/Either is exactly equivalent to choosing an exception taxonomy. You're doing the same work either way.

Re: Avoid exception throwing in performance-sensitive code

#205
post #149
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…

I long wished there was C# Intellisense that showed what kind of exceptions I may receive when calling particular function. At least from code it can infer from: - .NET library has documentation comments, with tags - It could look at my code and see what exceptions get thrown. - It could be clever enough to know that new might throw OutOfMemoryException - Clever enough to know checked arithmetic might throw OverflowE…

What you are asking for is checked exceptions where the caller gets notified by the compiler that the function you call might throw one or more exceptions.

However, it is worth taking it a step further and focus on "why the error occurred" instead of "an error has occurred".

An example of this is Code Contracts. Like Intellisense, live code analysis with contracts would tell you "the method you call will throw with the input you give".

Not only will that cover giving the user information about which error conditions that can arise, it will also give you the reason why.

Re: Avoid exception throwing in performance-sensitive code

#206

Earlier quoted context omitted.

Java got wrong with the concept of checked exceptions. They're not needed. Python, C++ or JavaScript exceptions are totally fine. And checked exceptions bring nothing but issues. The only thing that I'd add to the unchecked exceptions is noexcept with compile-time checking. Something along the lines: 1. You can declare method as `nothrows`. Compiler will ensure that no exceptions are thrown (`java.lang.Error` can sti…

I disagree that standard exceptions are fine for recoverable errors. Recoverable error states are part of your function's interface. Languages with exceptions make this implicit: in order to know how to interface with a function's error states you have to go digging through the docs, which hopefully document the exceptions it throws. If they don't, then you either have to trace the entire call tree or just wait for t…

The fact is that checked exceptions are avoided in modern Java. They are part of function interface, compiler forces you to handle or propagate it. Yet people didn't like it and actively sabotage this design. So this design didn't work. You can shove it down the peoples throat or you can design features that will help rather than irritate.

Re: Avoid exception throwing in performance-sensitive code

#207
post #193

Earlier quoted context omitted.

Java got wrong with the concept of checked exceptions. They're not needed. Python, C++ or JavaScript exceptions are totally fine. And checked exceptions bring nothing but issues. The only thing that I'd add to the unchecked exceptions is noexcept with compile-time checking. Something along the lines: 1. You can declare method as `nothrows`. Compiler will ensure that no exceptions are thrown (`java.lang.Error` can sti…

Java always gets the blame, yet the concept of checked exceptions was introduced by CLU, adopted by Modula-3 and C++, before it came to Java. And even though C++ dropped exception specifications, they still kept the difference between might throw anything or doesn't throw at all, and there is also the paper to reintroduce them Swift style.

Those are not industry languages and Java was known to borrow only well developed and widely adopted features. For example it took like 20 years to add lambdas which were in lisp 60 years ago. Checked exceptions are a sore exception to this rule.

Re: Avoid exception throwing in performance-sensitive code

#208
post #193

Earlier quoted context omitted.

Java always gets the blame, yet the concept of checked exceptions was introduced by CLU, adopted by Modula-3 and C++, before it came to Java. And even though C++ dropped exception specifications, they still kept the difference between might throw anything or doesn't throw at all, and there is also the paper to reintroduce them Swift style.

Those are not industry languages and Java was known to borrow only well developed and widely adopted features. For example it took like 20 years to add lambdas which were in lisp 60 years ago. Checked exceptions are a sore exception to this rule.

C++ isn't an industry language?!?

Re: Avoid exception throwing in performance-sensitive code

#209
post #128

Earlier quoted context omitted.

And it is still opaque where it goes to the programmer when they see it in code. The fact you can use tools to find where it might land (yeah, no shit, you can do same for goto...) is just a mitigation to the problem.

It unwinds the call stack until a catch. It is the same "problem" as not knowing where a return will return to.

Return only ever moves up the stack one level. That makes it really easy to reason about. This can move up the stack an unlimited amount, and the handler has to be prepared to properly deal with the current state, no matter where it comes from.

Re: Avoid exception throwing in performance-sensitive code

#210

Earlier quoted context omitted.

This is also how errors-as-values work in Rust. Functions that may fail return Result - putting an ? at the end of a failable function call returns T on success, otherwise it propagates E. It reduces result, err := call() if err != nil { return err } to let result = call()?; Completely unobtrusive, but makes failure awareness an obligation.

I know about ? in Rust - the one thing that isn't very clear to me is how often it is enough. That is, with Go, it's quite typical to do something like: result, err := call() if err != nil { return fmt.Errorf("Error while trying to call: %v", err) } Essentially manually building a stack trace. If just doing "return err", you end up with calls to a REST service failing with messages like `couldn't parse "" as int` eve…

if you only limit yourself to the standard library, you would need to unwrap and rewrap the error - although admittedly since Result is a type like any other, you could add an extension function (through a trait) so that you can add context more easily. If you use use one of the error handling libraries though, you're in luck - adding context is usually a single function call away:

    //without context
    some_function()?;
    //with context, using anyhow
    some_function().context("function returned error")?;
    //with wrap_err, using eyre
    some_function().wrap_err("function returned error eyre")?;
Post reply on HN