3 ms·
Not at all. Basically, a naive implementation of exceptions uses a lock to guard unwind tables, and this scales poorly. The lock is needed only because .so lib
by protomolecule 2y ago
Not at all.
Basically, a naive implementation of exceptions uses a lock to guard unwind tables, and this scales poorly. The lock is needed only because .so libraries can be loaded and unloaded dynamically. One solution is to say that after a certain moment, the program doesn't load/unload dynamic libraries and doesn't use the lock after that. That's what ScyllaDB and Yandex[0] do, for example. Another solution is to just use the new glibc which fixes this issue.
That aside, both exceptions and error codes introduce overhead, but they do it differently: exceptions add most[1] of their overhead when an error happens, while checking error codes adds overhead when an error doesn't happen[2]. One might conclude that exceptions should be used when errors are exceptionally rare; otherwise, use error codes. Or, just reclassify errors that happen too often as something you expect to happen, return them as a case of valid data and keep using exceptions.
[0] https://habr.com/en/companies/yandex/articles/852244/ https://habr.com/en/companies/yandex/articles/852244/
[1] https://habr.com/en/companies/jugru/articles/494986/ https://habr.com/en/companies/jugru/articles/494986/
[2] https://archive.is/bIU23 https://archive.is/bIU23
- adrian_b 2y agoChecking error codes adds overhead only in the way it is implemented in modern programming languages, by returning an integer error code that must be tested for a conditional jump after returning to the invoking procedure. Many early programming languages, including various dialects of Algol and Fortran, included a better implementation method. The invoking procedure passed to the invoked procedure not a single return address, but multiple return addresses. One return address was for the normal return and the other addresses were for alternate returns when errors were detected. On a modern CPU with many registers, all these return addresses would be passed in registers. The invoking procedure had at its end one or more handlers for error conditions, corresponding to the alternate return addresses. When a procedure was invoked, no tests were done at the invocation place, because any error would cause a jump to the error handler. In the invoked procedure, a test and a conditional jump must always exist in order to detect an error, and there the alternate return was executed only when an error was detected, so this implementation could omit one test and one conditional jump in comparison with the modern implementations. Moreover, the text of the procedure was more clear in this way, with all the error handlers separated from the normal execution case, but nonetheless close enough to examine them when necessary.
- protomolecule 2y agoThanks, that's an interesting idea, even though I'm not sure that extra instructions to put these return addresses into registers and reduced number of registers available for the codegen is better than cmp and jmp. Also I'm not sure if that will play well with cpu's branch predictor.