This is an archive of the discontinued LLVM Phabricator instance.

allow for contention free exception unwinding
Needs RevisionPublic

Authored by neumannt on Feb 21 2022, 4:35 AM.

Details

Reviewers
libcxx-commits
ldionne
rprichard
trcrsired
Group Reviewers
Restricted Project
Summary

The current unwinding logic uses global locks, which leads to
terrible exception performance in multi-threaded programs.
For example on an AMD EPYC 7713 with 128 cores we see a performance
degradation from 36ms single threaded to 6424ms with 128 threads
when throwing multi-threaded.
See https://isocpp.org/files/papers/P2544R0.html for a detailed
discussion.

This commit fixes this problem by maintaining the exception tables
in a b-tree using optimistic lock coupling, which allows for
lock-free reads. Writers still lock, but changes to exception tables
are rare, they are usually triggered only by dlopen/dlclose calls or
by JITed code. During unwinding that mechanism does not require any
atomic writes, which allows for running the test program mentioned above
in 59ms instead of over 6s.

Unfortunately the mechanism requires cooperation from the application,
as the glibc has no mechanism to notify us when a shared library is
removed. Thus, it is double opt-in: First, the libunwind itself has
to be compiled with -DLIBUNWIND_USE_BTREE=On. And then at runtime,
the application has to enable the mechanism by calling

void __libunwind_btreelookup_sync();

That sync function must be called once at startup and then again
after every dlopen and every dlclose call. Failing to call sync
after dlopen is safe, it can just lead to suboptimal performance by
falling back to the existing mechanism. But failing to call sync
between a dlclose/dlopen sequence is unsafe, as the new library
might remain associated with the old unwinder logic.

But if the application knows what it is doing, and guarantees to
call sync between dlclose and dlopen, it gets dramatically better
performance in exception unwinding.

Note that the current implementation is quit conservative, as it
leaves the ehframe tables as they are and just indexes the tables
themselves with a btree. Which means that the btree will usually
have a few of entries per shared library. An alternative design
would be to store the content of the ehframe tables in the btree,
just as with dynamic frames from JITed code. This would make unnesting
even faster, but would consume more memory, as we would have one entry
per function instead of one entry per library.

Diff Detail

Event Timeline

neumannt created this revision.Feb 21 2022, 4:35 AM
Herald added a reviewer: Restricted Project. · View Herald TranscriptFeb 21 2022, 4:35 AM
Herald added a subscriber: mgorny. · View Herald Transcript
neumannt requested review of this revision.Feb 21 2022, 4:35 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 21 2022, 4:35 AM
neumannt updated this revision to Diff 410293.Feb 21 2022, 6:57 AM

applied clang-format

neumannt updated this revision to Diff 410323.Feb 21 2022, 9:11 AM
neumannt edited the summary of this revision. (Show Details)

clang-format to libunwind.cpp, and fix comments

trcrsired requested changes to this revision.EditedFeb 22 2022, 9:28 PM
trcrsired added a subscriber: trcrsired.

I am curious about why LLVM refuses to implement herbceptions. I do not think any of these patches are going to fully address the issue. My issues with EH are that EH is simply not available in my environment due to implementation difficulties. If you think the issue is so severe as I do, why not just start working on P0709?

This revision now requires changes to proceed.Feb 22 2022, 9:28 PM

I am curious about why LLVM refuses to implement herbceptions. I do not think any of these patches are going to fully address the issue. My issues with EH are that EH is simply not available in my environment due to implementation difficulties. If you think the issue is so severe as I do, why not just start working on P0709?

I have tried that, too, but I encountered two problems: 1) herbcepations were painfully invasive to introduce in our code base, as they are not source code compatible. For example a void (*)() function ptr is not compatible with a void (*)() throws function ptr. And potentially throwing paths tends to turn up everywhere, when I changed one innocently looking functions to "throws" I ended up changing over 230 files as a consequence. That makes it very difficult to introduce, I think we need a better compatibility story for that before herbceptions are ready for prime time. And 2) third party code continues to throw traditional exceptions, including the standard library.

Thus, we will need to fix the unwinding issue, too, independent of herbceptions. Simply because existing code continues to need that unwinding logic.

trcrsired added a comment.EditedFeb 22 2022, 10:43 PM

I am curious about why LLVM refuses to implement herbceptions. I do not think any of these patches are going to fully address the issue. My issues with EH are that EH is simply not available in my environment due to implementation difficulties. If you think the issue is so severe as I do, why not just start working on P0709?

I have tried that, too, but I encountered two problems: 1) herbcepations were painfully invasive to introduce in our code base, as they are not source code compatible. For example a void (*)() function ptr is not compatible with a void (*)() throws function ptr. And potentially throwing paths tends to turn up everywhere, when I changed one innocently looking functions to "throws" I ended up changing over 230 files as a consequence. That makes it very difficult to introduce, I think we need a better compatibility story for that before herbceptions are ready for prime time. And 2) third party code continues to throw traditional exceptions, including the standard library.

Thus, we will need to fix the unwinding issue, too, independent of herbceptions. Simply because existing code continues to need that unwinding logic.

"For example a void (*)() function ptr is not compatible with a void (*)() throws function ptr" is a bad excuse tbh.
We already have that kind of issues being. void (*)() noexcept is not be compatible with void (*)();
In fact the situation is already extremely bad for current EH when i have to use noexcept_call everywhere.
https://github.com/cppfastio/fast_io/blob/ff7e3865520e92129c916915ecb853e65546cf6c/include/fast_io_core_impl/utils.h#L78

Please stop wasting time on fixing C++ EH. Start to work on herbceptions. I use llvm mainly for compiling wasm programs and a lot of other environments which don't have the EH support. Adding "fix" to libunwind is not going to address any real issue with EH while adding an inifinite amount of pain to support new platforms (like wasm for example).

Time to deprecate C++ EH tbh. Even LLVM itself does not use EH.

Please stop wasting time on fixing C++ EH. Start to work on herbceptions. I use llvm mainly for compiling wasm programs and a lot of other environments which don't have the EH support. Adding "fix" to libunwind is not going to address any real issue with EH while adding an inifinite amount of pain to support new platforms (like wasm for example).

Time to deprecate C++ EH tbh. Even LLVM itself does not use EH.

This topic is unrelated to the patch at hand and should rather be brought up with the C++ committee or with other contributors interested in working on alternative models, like herbceptions, instead of trying to dissuade others like the Author here from improving the status quo.
libunwind in particular is also not technically specific to C++, even if the various implementations of the C++ ABI Support libraries are large users of it and would benefit a lot from it.

Please stop wasting time on fixing C++ EH. Start to work on herbceptions. I use llvm mainly for compiling wasm programs and a lot of other environments which don't have the EH support. Adding "fix" to libunwind is not going to address any real issue with EH while adding an inifinite amount of pain to support new platforms (like wasm for example).

Time to deprecate C++ EH tbh. Even LLVM itself does not use EH.

This topic is unrelated to the patch at hand and should rather be brought up with the C++ committee or with other contributors interested in working on alternative models, like herbceptions, instead of trying to dissuade others like the Author here from improving the status quo.
libunwind in particular is also not technically specific to C++, even if the various implementations of the C++ ABI Support libraries are large users of it and would benefit a lot from it.

Yes, it is highly related to herbceptions because current exception is simply just impossible to use. When are you going to provide eh support for wasm? How are you fixing issues when half of my cross toolchains have no eh support?

No, you are not fixing problems. You actually give standard committee excuses for continuing this totally completely broken model, as Linus Torvalds suggested 20 years ago.

On glibc 2.35 and later, you can use _dl_find_object. It's also fully lock-free.

Fedora 36 already has a GCC version built against this if you want to run benchmarks. A container image is at registry.fedoraproject.org/fedora:36.

On glibc 2.35 and later, you can use _dl_find_object. It's also fully lock-free.

_dl_find_object does not help with dynamic frames. But I could update my patch to use the btree only for dynamic frames registered via __unw_add_dynamic_fde and to use _dl_find_object for static exception frames.