4 ms·
C++ exceptions and by extension Rust panics and C# exceptions (with caveats) involve stack unwinding as well as gathering of corresponding details and producing
by neonsunset 4y ago
C++ exceptions and by extension Rust panics and C# exceptions (with caveats) involve stack unwinding as well as gathering of corresponding details and producing an exception object (C#). It is very expensive and using it as a normal condition in general is not the best idea. It is by definition cannot be optimized in a way that gets both good performance and keeps all existing side effects.
That's why Rust's Result<T, E> is vastly superior because it out of box pushes the users to very efficient and idiomatic error handling mechanism which is based on its enum types that enjoy a lot of compiler optimizations.
You can also achieve fairly good results with struct-based custom Result<T, E> implementations in C# but until the language gets the support of proper discriminated unions, it will always be inferior.
Thankfully, in C# and, I assume, in other exception-based languages the compiler is smart enough to recognize exceptions as cold paths and correctly reorder emitted code to minimize their impact when you do not throw them. But traversing try-catch blocks still tends to pessimize the codegen significantly hence heavy reliance of C# on various throw helpers to keep hot paths clean.
- User23 4y agoI don't know how it is now, but back in the day C++ exceptions were so messy and non-performant that Google forbid their use in company C++ code. And they employed at least one member of the standards committee!
- photochemsyn 4y agoLooks to still be the case: https://google.github.io/styleguide/cppguide.html#Exceptions https://google.github.io/styleguide/cppguide.html#Exceptions One could argue this is something of a technical debt issue, as the rationale notes: > "Given that Google's existing code is not exception-tolerant, the costs of using exceptions are somewhat greater than the costs in a new project. The conversion process would be slow and error-prone. We don't believe that the available alternatives to exceptions, such as error codes and assertions, introduce a significant burden." > "Our advice against using exceptions is not predicated on philosophical or moral grounds, but practical ones. Because we'd like to use our open-source projects at Google and it's difficult to do so if those projects use exceptions, we need to advise against exceptions in Google open-source projects as well. Things would probably be different if we had to do it all over again from scratch."
- azov 4y ago"On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code, the introduction of exceptions has implications on all dependent code. If exceptions can be propagated beyond a new project, it also becomes problematic to integrate the new project into existing exception-free code. Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions." (https://google.github.io/styleguide/cppguide.html#Exceptions) So, basically, Google forbids exceptions for historical reasons, not because of performance. But, sadly, countless companies parroted this section of Google's style guide for all the wrong reasons (mostly just cargo culting Google) leading to unfortunate fragmentation of error handling in the C++ library ecosystem.
- kevingadd 4y agoIn the C# stdlib there is at least currently the Task and ValueTask pair, where the latter is a struct optimized for 'failure is rare and success is synchronous' that degrades smoothly to the former, which can represent asynchronous computation and lets you recover failure exceptions w/stack without having to ever actually throw. I do wish the async part was decoupled from it, but it's nice that all the error information is preserved if used correctly.
- neonsunset 4y agoWhile correct in terms of convenience, it still wraps an exception with all associated costs. But yet again, you can hand-roll custom `Result<T, E>` even today and implement something like `.Wrap(Func<T> func)` that would produce `Result<T, Exception>` to interoperate. Anyway, if the callee throws an exception, it will be just as costly as any other even if you can examine the `Value/Task<T>` for `.IsFaulted` and `.Exception`. Possibly counterintuitively, if thrown, it will also dwarf the overhead of allocating state machine object and executing `MoveNext()` decoration. Hence even in async there is value to be had in not using exceptions in performance-sensitive scenarios.
- cl0ckt0wer 4y agoNick Chapsas did a demo of just how much overhead regularly thrown exceptions cause: https://www.youtube.com/watch?v=2f2elFRmeLE https://www.youtube.com/watch?v=2f2elFRmeLE Spoiler, each throw/catch costs 20 microseconds