Live data from Hacker News

Avoid exception throwing in performance-sensitive code

lemire.me

61–70 of 243 posts

Re: Avoid exception throwing in performance-sensitive code

#61
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.

Re: Avoid exception throwing in performance-sensitive code

#62
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 in it. Each block of code that ran between the form display and wait for input block turned into a function in C#, and all of them were chained in the main function with gotos. To get out of the function, the transpiled code would throw new Exception("with some custom message indicating step to go to next"), it'll be caught upstack, message parsed and then goto'ed to that. So yeah, control flow with Exceptions

I am shuddering just thinking about it now, it was impossible to read. Under medium load - and I was in charge of load testing - it threw some astronomical numbers of exceptions/sec and it was just so ugly.

Try as I might I couldn't dissuade them to change the ways they returned from the function. It all went to production. The customer just threw really big machines and lots of them and they just exceptioned all day long.

Re: Avoid exception throwing in performance-sensitive code

#63
I think what the article is addressing is likely not what is being discussed in this thread. The article ("Unfortunately, I often see solutions abusing exceptions" followed by using exceptions to replace if statements) is very likely discussing poor understanding of the topic from student assignment rather than actual code in the industry. Such mistakes (abuse of exceptions in the article) often go away once students become actual professionals because these mistakes are more easily caught during code review than other types of common mistakes.

Re: Avoid exception throwing in performance-sensitive code

#64
post #29

Earlier quoted context omitted.

> Seems like a language design flaw that this is slow I think stack traces take up much of the time, including converting it [1]. Without them it could probably be a lot faster. Also see hashmash's post. But other than that, there is the logic issue, not using exceptions for normal flow control makes sense at least to me too independent of any performance questions. [1] For a Java example: https://ionutbalosin.com/20…

IIRC, that's why Go errors don't come with stack traces by default, performance. I'll admit that this has enraged me on a few occasions when all I have to work with is a logged error message of strconv.ParseInt: parsing "": invalid syntax With no clues as to where the hell the error actually happened, so I have to start grepping the app's code, then the code of its dependencies, then the code of the dependencies' dep…

A stack trace would be helpful, but so would adding context to the error (when appropriate), instead of just bubbling it up indiscriminately.

Of course, you can’t enforce this is dependencies, but at least tracking this to the first layer dependencies of your code should be fairly easy right?

TBH, Go style errors seem more flexible if some care is put into using them, however they are extremely unhelpful if they are used improperly (even if it’s not your own code).

Re: Avoid exception throwing in performance-sensitive code

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

> 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 generics. Even today checked exceptions are horribly cumbersome - for example, 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.

If Java had had proper ML-style types from day 1, like Rust does, then it could have done "errors are values" in a practical way. But forcing people to use checked exceptions would have resulted in either C-style errno codes, or the whole language collapsing under its own weight.

Re: Avoid exception throwing in performance-sensitive code

#66
post #36
post #29

Earlier quoted context omitted.

> Seems like a language design flaw that this is slow I think stack traces take up much of the time, including converting it [1]. Without them it could probably be a lot faster. Also see hashmash's post. But other than that, there is the logic issue, not using exceptions for normal flow control makes sense at least to me too independent of any performance questions. [1] For a Java example: https://ionutbalosin.com/20…

This is another reason why Common Lisp style condition system (where exception handlers can execute without unwinding the stack) just seems to work better. If you need the stack, get the stack, if you don't then just use handler-case. I really don't see the downside for any language that has anonymous function literals.

Java has them now but it didn't at the time (and even now they're kind of bodged in IIRC).

Re: Avoid exception throwing in performance-sensitive code

#67
Not having exception is not having incorrect code i.e. code that does not throw. This is very hard to do in languages that naturally throws exception like Java or C/C++. If you have errors as part of the contract however...now you can be sure (or surer) that the function you are calling does not fail.

Re: Avoid exception throwing in performance-sensitive code

#68
post #52
post #24

excepts were the only way in CLU to handle control flow. something like while read_character { handle_character } except_when end_of_file { return string } and it did this primarily because it was typesafe: read_character always returned a character, and the "except" cases could return what they were declared to return. "except" was far from exceptional. There's nothing ineffecient about that, it's meatly linked up a…

Returning an option sumtype is also typesafe. Exceptions have to walk up the stack until a suitable handler is found, that handler can't know ahead of time where the value is coming from or if it will ever arrive - just what type it will be if it arrives. Code emitting exceptions also have no knowledge who (if anyone) is going to handle their output. It is a nonlocal goto in reverse. Compared to regular functional re…

That's one way to compile exceptions. There are other methods with different tradeoffs for different cases.

Re: Avoid exception throwing in performance-sensitive code

#69

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…

Seems like a language design flaw that this is slow. Imo Rust got this right by making "exceptions" nothing special, just a data type holding an error which can be processed just as fast as anything else.

When exceptions are used for actual exceptional cases, that is both slower in the happy path (as you are having to add a ton of comparisons for the error case, vs. zero-cost exceptions as are now commonly deployed) and more verbose (though Rust has been working to mitigate this over the years, they still failed to realize the people who made the kind of error object popular also designed an abstraction for monads and a syntax sugar that made using the result/either object "look like" exceptions) than exceptions.

Re: Avoid exception throwing in performance-sensitive code

#70
post #53

Earlier quoted context omitted.

Food for thought: what are the expected consequences of the exception? If the error will stop the program flow and show a warning dialog to the user, it's useless to think too much about performance. More or less the same if it's going to log some message and abort the operation. What is usually frowned upon is using the exception as a kind of goto for normal flow of the program. Exceptions should be... the exception…

"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.
Post reply on HN