Live data from Hacker News

Avoid exception throwing in performance-sensitive code

lemire.me

141–150 of 243 posts

Re: Avoid exception throwing in performance-sensitive code

#141

Earlier quoted context omitted.

I indeed like Rust's approach a lot more than Go's. What I like less still is that it gives the impression that it's even possible to define functions that cannot fail. This is not true. One just has to look at how runtimes deal with stack overflow errors to see how the good old Java RuntimeException creeps in in various forms (e.g. panics) because checked exceptions and it's recent incarnation as error values are a…

> it gives the impression that it's even possible to define functions that cannot fail Do you mean that e.g. an out-of-bounds error will panic? If that's the case, you can always access arrays/slices with some checked access, that will return a Result/Option and cannot panic. But it would be a PITA if you couldn't skip that.

I mean stack overflows or out of memory errors. It might fail one request, but no reason to fail all others.

Re: Avoid exception throwing in performance-sensitive code

#142

Earlier quoted context omitted.

Rust do still has exceptions, they're just called "results". You can't really have values as exceptions unless you plug a lot of ad-hoc constructs into your language so that they eventually become similar to exceptions [1]: > Add syntactic sugar for working with the Result type which models common exception handling constructs. The whole error handling story with Rust can be summarized as "we have results but want to…

Errors in rust are not exceptions, they're data contained within an `Either` monad (Result) Additionally, on top of the linked RFC being nearly 9 years old it doesn't at all indicate "we want to have exceptions". The ? operator allows propogating the errors if they can't be immediately handled (similar to monadic `do` notation, or early returns)

Just because Rust people are in denial about it doesn't make them not exceptions. The Either/Result monad is isomorphic to checked exceptions, with any differences being pretty much just syntax.

Re: Avoid exception throwing in performance-sensitive code

#143

Earlier quoted context omitted.

You seem to believe options B to D are not available to programmers in languages with exceptions. The real value comes from making option E much more unlikely: ignoring both the result and error value altogether, because you relied on the side effect of the function you called.

I'm talking about unchecked exceptions. It's not about what is possible it's about what patterns a language encourages. It feels like we've lost the thread of discussion. - You say there is no difference between unchecked exceptions and Go's errors - I say yes there is since Go forces users to handle errors explicitly - You say that's not technically true in all cases. OK. Yes. I should have said "nudges users" inste…

> You say there is no difference between unchecked exceptions and Go's errors

Where do I say that? I say that Go programmers, in 99.9% of cases, do manually what exceptions do automatically. In terms of cumbersome, error values are the equivalent of checked exceptions. The equivalent of runtime exceptions are panics.

Re: Avoid exception throwing in performance-sensitive code

#144
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 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".

Catching unchecked exceptions at module boundaries is perfectly fine and as far as I know (which admittedly isn't much) both Go and Rust in fact allow doing exactly that.

Re: Avoid exception throwing in performance-sensitive code

#145
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…

Yes, checked exceptions with a proper type system and abstraction facilities would be completely equivalent to Result, except built into the language.

Re: Avoid exception throwing in performance-sensitive code

#146
post #84

Earlier quoted context omitted.

> where library code can suddenly abort and the author shrugs and says "shoulda read the docs". In see no difference. Except for the fact that you don't have the stacktrace (in Go), and unroll the Stack manually. In any case, there'll be a generic catch (whatever you call it) on the top level of your daemon to recover from the failed request and handle the next.

In Go the programmer is forced to make a decision on what to do with the error. Common patterns include (A) return an annotated error, (B) log it and continue, (C) retry, (D) aggregate the errors in some way. If you believe the only way to handle an error is (A), then Go's design makes no sense.

> In Go the programmer is forced to make a decision on what to do with the error.

Actually go runs perfectly fine if you just do nothing with the error, and then you are flying past errors like it is nobody's business. Of course you can you static analysis to tell you if you forgot to handle an error, and most people do, but that is not really a language feature.

In Java a programmer is also forced to make a decision to add explicit exception handling or not. But if they choose to not add it, the program won't just pretend like all is fine and dandy, it will stop the execution of the current function and pass the exception up the stack.

Re: Avoid exception throwing in performance-sensitive code

#147

This is one of my basic interview questions to candidates and it is amazing how many people have no idea how exceptions work or how much they cost. Back in 2005, I was involved in a replatforming of the legacy COBOL and TRANSACT HP3000 mainframe codebase to the modern system. The code was transpiled into an [unholy mess of] C# and ASP.NET. The Transact code was extremely procedural and had mainframe forms interspread…

That seems exceptionally bad!

Re: Avoid exception throwing in performance-sensitive code

#148

This post is poorly named. What it's saying is to avoid throwing exceptions for normal flow control, and just use them for exceptional cases (file not found, etc). Performance-sensitive code or not, exceptions for exceptional situations are not going to hinder the app's performance until the exceptional situation becomes common (unless your language does lots of exception setup work on the happy path - which most hav…

This! I'd like to think exceptions are reasonable as a terminal state for a particular user workflow, which sometimes even end up having the program terminated. Examples include "unexpected I/O, corrupt files etc." Exceptions should never be used if the control flow has means to recovery. For example, if-else statements with more different, downstream instructions.

What about, say, a webhook server that must call User-provided webhooks, and retry them on failures, or handle maximum request duration?

I’d argue throwing well-nested exceptions, catching them and retrying is the most elegant solution to this problem there is.

Re: Avoid exception throwing in performance-sensitive code

#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 OverflowException

- Might understand where/what exceptions get caught and do not bubble up.

- Smart enough to understand if value can possibly be null and properties are accessed, thus may result in ArgumentNullException.

- As for 3rd party code, I don't know if .pdb provide enough info to know whether particular function call may throw particular exception? But it's all IL, so that should be enough to infer from 3rd party code what kind of exceptions I might encounter:

   // throw new ArgumentNullException("serviceProvider");
   IL_0013: ldstr "serviceProvider"
   IL_0018: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string)
   IL_001d: throw

Eh, what a dream.

Re: Avoid exception throwing in performance-sensitive code

#150

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 allow concurrent unwinding without breaking the ABI (but it is hard).
Post reply on HN