Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
attach two attributes to
folly::coro
to improve elidability
Summary: # Context Recent upstream LLVM development gives us this opportunity to elide heap allocations for coroutine function calls. Our folly::coro's heap allocation costs us about $4M every year fleet wide. This patch potentially gives us a decent chunk of capacity savings. This diff adds two attributes to folly: ## [[clang::coro_await_elidable]] This attribute is introduced by llvm/llvm-project#99282. This attribute is designed to make the following case always apply HALO ``` class [[clang::coro_await_elidable]] Task { ... }; Task<void> foo(); Task<void> bar() { co_await foo(); } ``` This requires 1) bar is a coroutine returns a type attributed as such; and 2) `foo()` to be immediately co_awaited. Note: if the example above had `bar()` call itself recursively, the elide won't happen because we cannot determine the necessary frame size for potentially unbound recursion. Same goes for mutually recursive functions. This won't work on the fan out case for the same reason. This optimization is off limits if we can't statically determine the necessary frame size for the outer most coroutine. ## [[clang::coro_await_elidable_argument]] This attribute is introduced by llvm/llvm-project#108474. The attribute propagates a "safe elide context" as defined in the summary of the PR. The motivation of this attribute is to support eliding foo in the following case: ``` class [[clang::coro_await_elidable]] Task { ... }; Task<void> foo(); Task<void> bar() { co_await co_nothrow(foo()); } ``` `co_nothrow` itself is not a coroutine. `foo()` in this case is not immediately `co_await`ed. Hence not elidable. However, if we add this "coro_await_elidable_argument" attribute to co_nothrow's parameter (or parameter pack), we allow the compiler to treat its arguments as safe to elide as well as long as both co_nothrow() is awaited as a prvalue, and `foo()` is immediately passed to `co_nothrow`'s param as a prvalue. This can also be useful for composition of senders in the future. ## Why is this safe to elide? Generally, we depend on the fact that `folly::coro::Task` does not provide the user a way to destroy the caller while the callee is in a running or leave it in a resumeable state. And `co_nothrow`'s awaitable object doesn't try to destroy caller either. ## How many more cases are elided after this change? IG is the top coroutine user according to this [strobelight](https://fburl.com/strobelight/dgx7cyzv). With this change, we found 3009 out of 4292 coroutine calls are elidable. Among the 1283 non-elidable cases, only 2 had a profile hotness greater than zero. Reviewed By: yfeldblum Differential Revision: D58956574 fbshipit-source-id: da8be360a03a34dec7111004a1af44e20d163839
- Loading branch information