What is an exception and why do you need them?

If you care about exceptions and error handling performance impact, then you probably already know what’s the deal! So I will happily skip this section 😁. This will serve primarily as notes for myself, but you might find it useful too.

General error handling approaches

There are few general approaches how to handle errors when programming. Not all are applicable to all programming languages though, since for example C does not support exceptions paradigm (sometimes this paradigm is not even called an exception paradigm. For example Swift calls it simply errors). Let’s check it out:

1) No error handling

Your program will likely crash, so this approach is not particularly useful.

2) Look before you leap (LBYL)

This is just fancy term for checking preconditions before actually executing potentially problematic piece of code. And the previous sentence is just fancy way how to say: use if/else. This is the only approach you can use for example in C.

Want to divide some numbers? LBYL approach here is checking if the divisor is not 0 prior to division to prevent zero-division error.

Want to open file for reading? LBYL is checking that the file exist before opening to prevent error.

3) Easier to ask forgiveness than permission (EAFP)

This is another fancy term for approach counterpart to LBYL, where you try to execute potentially problematic operation, but wrapping this operation into error/exception handling statements (known for example as try/catch/try/except/do/catch etc.).

In case there is an error, you handle this error or recover in the error handling block and then potentially continue with execution of the program. You can use this approach in languages that support exceptions paradigm, i.e. Java, PHP, Python, Swift etc.

Great, so what approach should I use?

When I started programming, I have read many articles stating that exceptions are expensive, so you should not use them for program control flow, along with many other reasons why not to use them if not necessary. The situation nowadays is quite different, at least for some languages. Over time, language compilers and interpreters gets better and better in terms of performance, so the situation changed a little. As with everything, the answer is it depends. It depends on language used and also on code you are writing.

Some programming languages even talk about “zero-cost” or “cheap” exceptions - however, you have to keep in mind, that it’s usually true only in case no exception is thrown in the code surrounded by exception handling statements (i.e. try/catch).

When searching for info how exceptions work in various programming languages, I initially wanted to check the behavior in source code of said languages. However, that proved to be very C, C++, or machine code related stuff, which is not my expertise, so I cowardly run away.

There is great article that explains LBYL and EAFP. It even show some useful code examples and discusses when it is better to use which of the two paradigms and even though the article is written for Python, there is a lot of general, language independent useful information.

Exceptions in Python

You can find out here How does exceptions work from the high level in python . The docs are not very detailed though. Python glossary somehow admits, that it is more common to use EAFP model in Python.

Somebody here did work for me and did the experiments, which concludes, that if no exception occurs within try/except block, it is overall very fast (in contrast to if statement, which must be evaluated every time). In case the exception is triggered, the overhead is much higher, but it all boils down to how likely is that this situation occurs -> If getting an exception is really exceptional, you should use EAFP, otherwise it might be better to use LBYL.

Exceptions in PHP

Again, Somebody here did work for me and the takeaway is the same - using try/catch over code that normally doesn’t raise exceptions is faster than using if statement everytime.

Errors in Go

As per today’s Go version (1.18), there are no exceptions in Golang. Go takes kinda simple approach to errors. Excerpt from excellent blogpost about Go's errors :

By Go’s convention, every function that can cause an error returns one as the last return value, and it is programmer’s responsibility to handle it properly at each step

This means, that almost every function’s return value needs to be checked (and handled) for possible error, meaning evaluating many if conditions in your program. According to this article here it, of course, adds some small overhead to your program (mainly because Go does not have pass by reference semantics ). However, when your program will do some heavy stuff, computations or whatever, this will be significantly more expensive than error checking ifs conditions, rendering them almost negligible to overall program performance.

There is another way how to possibly deal with errors in Go. That is defer/panic/recover mechanism, (which is basically try/catch block, but that is scoped to whole function, not just to certain code block in function) which might be sometimes faster than Go’s “normal” error handling way, but it depends heavily on your program’s workload and in my point of view it is just form of optimization that might make sense in some programs.

Errors in Swift

Error handling in Swift is, in principle, very similar to Go’s. Citing from this blogpost about how errors in Swift work internally

Swift returns thrown errors in a special (CPU) register, and the caller checks that register to see if an error has been thrown. This adds a bit of overhead when errors aren’t thrown, but avoids making things enormously complicated the way C++ does.

The difference between Go’s error propagation mechanism is that Swift does not force you to read the error register (error return value) manually like Go does (it is done under the hood automatically), and in case error occurred in the do block, Swift hands control over to catch block to handle the error without expensive call stack unwinding like for example in C++ and Java. So in general, Swift error handling mechanism is relatively performance-friendly too.

Conclusion

The information in this post is not a big bang in any way. However, it was a nice exercise to get some high-level overview about the topic and somebody might find it useful, too. Keep in mind that I have examined only some languages I personally use and others might have completely different behavior/performance impact/recommendations.

In general, from the data I have gathered, as far as the error handling performance is concerned, using try block (EAFP) for code that normally doesn’t raise exceptions is faster than using if statement everytime.

So, the decision depends on the probability of exceptional cases.