This is an archive of the discontinued LLVM Phabricator instance.

[Support] Make llvm::Error faster
ClosedPublic

Authored by zturner on Nov 7 2017, 1:54 PM.

Details

Summary
Whenever LLVM_ENABLE_ABI_BREAKING_CHECKS is enabled, which
is usually the case for example when asserts are enabled,
Error's destructor does some additional checking to make sure
that that it does not represent an error condition and that it
was checked.

However, this is -- by definition -- not the likely codepath.
Some profiling shows that at least with some compilers, simply
calling assertIsChecked -- in a release build with full
optimizations -- can account for up to 15% of the entire
runtime of the program, even though this function should almost
literally be a no-op.

The problem is that the assertIsChecked function can be considered
too big to inline depending on the compiler's inliner.  Since it's
unlikely to ever need to failure path though, we can move it out
of line and force it to not be inlined, so that the fast path
can be inlined.

In my test (using lld to link clang with CMAKE_BUILD_TYPE=Release
and LLVM_ENABLE_ASSERTIONS=ON), this reduces link time from 27
seconds to 23.5 seconds, which is a solid 15% gain.

Diff Detail

Event Timeline

zturner created this revision.Nov 7 2017, 1:54 PM
ruiu accepted this revision.Nov 7 2017, 2:00 PM
ruiu added a subscriber: ruiu.

Nice. LGTM

llvm/include/llvm/Support/Error.h
284–285

I believe clang-format-diff would format it in one line.

This revision is now accepted and ready to land.Nov 7 2017, 2:00 PM
hans added a subscriber: hans.Nov 7 2017, 2:13 PM
hans added inline comments.
llvm/include/llvm/Support/Error.h
269

Would it be better to put this in the .cpp file instead? Since it's not supposed to be inlined, there's no point in declaring it inline, I think.

hans removed a reviewer: hansw.Nov 7 2017, 2:14 PM
hans added inline comments.Nov 7 2017, 2:36 PM
llvm/include/llvm/Support/Error.h
269

Oh, and it's probably worth putting LLVM_ATTRIBUTE_NORETURN on it.

lhames edited edge metadata.Nov 7 2017, 2:44 PM

Nice!

Aside: getChecked() should be a no-op in release builds, but we do still check the error pointer for equivalence with uncaught exceptions. (I'm open to revisiting that idea: we also have better error checking validation than exceptions provide).

So does the 15% speed-up mean that you're now spending ~0% in Error destructors?

zturner updated this revision to Diff 122020.Nov 7 2017, 4:50 PM

I went ahead and applied the same optimization for llvm::Expected, and also incorporated other suggestions. Even though Expected wasn't showing up on the profile, this only arose because we happened to be a heavy user of Error. If someone else happened to be a heavy user of Expected, we'd see the same issues arise.

To answer @lhames question about the Error destructor, It's hard for me to check the total time in Error's destructor, or at least I don't know how to do it with this particular profiler. I can either get exclusive samples, in which case the number won't account for nested calls inside of the destructor, or I can get inclusive samples, in which case I'll need to find every callsite of an Error destructor. I'm sure there's a way, I just don't know this profiler tht well.

What I can say though is that before the patch, on one particular run lld-link had 23,271 total samples, 2,017 samples of which were in assertUnchecked. After the patch, lld-link had 25,493 total samples, 0 of which were in assertUnchecked (i.e. it wasn't even on the profile).

ruiu added inline comments.Nov 7 2017, 6:36 PM
llvm/include/llvm/Support/Error.h
640

Maybe you should move this to a .cpp file as well?

hans accepted this revision.Nov 7 2017, 8:01 PM

lgtm

Maybe mention llvm::Expected in the commit message too.

zturner closed this revision.Nov 9 2017, 11:32 AM