Commit 23d4c5e2 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

dispatch indirection for function exec

Summary:
The type-erased Function is widely used with lambdas and the type of every lambda is unique. Template instantiations are costly for build speed so, for the parts of Function which interact with lambdas, we choose techniques to optimize build speed.

In particular, we avoid the extra layer of constructor templates which must be overload-tested and instantiated. The existing design of the extra layer was clean - it effectively used tag-dispatch and was able to deduplicate code with the coercing constructor. However, the choice of techniques was not optimal for build speed.

```name=trunk
$ foundation/scripts/run-perf-compile --syntax folly/futures/test/FutureTest.cpp
     6,407,492,259      instructions:uP
```

```name=branch
$ foundation/scripts/run-perf-compile --syntax folly/futures/test/FutureTest.cpp
     6,303,165,111      instructions:uP
```

Reviewed By: Gownta, ot, luciang

Differential Revision: D32940920

fbshipit-source-id: 742cb67b994e7c3e6c070254386f3cdf5dffa38c
parent 7e27b793
...@@ -260,8 +260,6 @@ template <typename Fun, typename = Fun*> ...@@ -260,8 +260,6 @@ template <typename Fun, typename = Fun*>
using IsSmall = Conjunction< using IsSmall = Conjunction<
bool_constant<(sizeof(Fun) <= sizeof(Data::tiny))>, bool_constant<(sizeof(Fun) <= sizeof(Data::tiny))>,
std::is_nothrow_move_constructible<Fun>>; std::is_nothrow_move_constructible<Fun>>;
using SmallTag = std::true_type;
using HeapTag = std::false_type;
struct CoerceTag {}; struct CoerceTag {};
...@@ -582,8 +580,12 @@ using Exec = decltype(&exec_); ...@@ -582,8 +580,12 @@ using Exec = decltype(&exec_);
static_assert(noexcept(Exec(nullptr)(Op{}, nullptr, nullptr)), ""); static_assert(noexcept(Exec(nullptr)(Op{}, nullptr, nullptr)), "");
#endif #endif
template <typename Fun> struct DispatchSmall {
std::size_t execSmall(Op o, Data* src, Data* dst) noexcept { template <typename Fun, typename Base>
static constexpr auto call = Base::template callSmall<Fun>;
template <typename Fun>
static std::size_t exec(Op o, Data* src, Data* dst) noexcept {
switch (o) { switch (o) {
case Op::MOVE: case Op::MOVE:
::new (static_cast<void*>(&dst->tiny)) ::new (static_cast<void*>(&dst->tiny))
...@@ -596,10 +598,15 @@ std::size_t execSmall(Op o, Data* src, Data* dst) noexcept { ...@@ -596,10 +598,15 @@ std::size_t execSmall(Op o, Data* src, Data* dst) noexcept {
break; break;
} }
return 0U; return 0U;
} }
};
struct DispatchBig {
template <typename Fun, typename Base>
static constexpr auto call = Base::template callBig<Fun>;
template <typename Fun> template <typename Fun>
std::size_t execBig(Op o, Data* src, Data* dst) noexcept { static std::size_t exec(Op o, Data* src, Data* dst) noexcept {
switch (o) { switch (o) {
case Op::MOVE: case Op::MOVE:
dst->big = src->big; dst->big = src->big;
...@@ -612,7 +619,8 @@ std::size_t execBig(Op o, Data* src, Data* dst) noexcept { ...@@ -612,7 +619,8 @@ std::size_t execBig(Op o, Data* src, Data* dst) noexcept {
break; break;
} }
return sizeof(Fun); return sizeof(Fun);
} }
};
} // namespace function } // namespace function
} // namespace detail } // namespace detail
...@@ -624,8 +632,6 @@ class Function final : private detail::function::FunctionTraits<FunctionType> { ...@@ -624,8 +632,6 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
// namespace for convenience. // namespace for convenience.
using Data = detail::function::Data; using Data = detail::function::Data;
using Op = detail::function::Op; using Op = detail::function::Op;
using SmallTag = detail::function::SmallTag;
using HeapTag = detail::function::HeapTag;
using CoerceTag = detail::function::CoerceTag; using CoerceTag = detail::function::CoerceTag;
using Traits = detail::function::FunctionTraits<FunctionType>; using Traits = detail::function::FunctionTraits<FunctionType>;
...@@ -655,30 +661,16 @@ class Function final : private detail::function::FunctionTraits<FunctionType> { ...@@ -655,30 +661,16 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
Function<typename Traits::NonConstSignature>&&) noexcept; Function<typename Traits::NonConstSignature>&&) noexcept;
friend class Function<typename Traits::OtherSignature>; friend class Function<typename Traits::OtherSignature>;
template <typename Fun> template <typename Signature>
Function(Fun&& fun, SmallTag) noexcept { Function(Function<Signature>&& fun, CoerceTag) {
using FunT = typename std::decay<Fun>::type; using Fun = Function<Signature>;
if (!detail::function::isEmptyFunction(fun)) { if (fun) {
::new (static_cast<void*>(&data_.tiny)) FunT(static_cast<Fun&&>(fun)); data_.big = new Fun(static_cast<Fun&&>(fun));
call_ = &Traits::template callSmall<FunT>; call_ = Traits::template callBig<Fun>;
exec_ = &detail::function::execSmall<FunT>; exec_ = detail::function::DispatchBig::exec<Fun>;
}
}
template <typename Fun>
Function(Fun&& fun, HeapTag) {
using FunT = typename std::decay<Fun>::type;
if (!detail::function::isEmptyFunction(fun)) {
data_.big = new FunT(static_cast<Fun&&>(fun));
call_ = &Traits::template callBig<FunT>;
exec_ = &detail::function::execBig<FunT>;
} }
} }
template <typename Signature>
Function(Function<Signature>&& that, CoerceTag)
: Function(static_cast<Function<Signature>&&>(that), HeapTag{}) {}
Function(Function<typename Traits::OtherSignature>&& that, CoerceTag) noexcept Function(Function<typename Traits::OtherSignature>&& that, CoerceTag) noexcept
: call_(that.call_), exec_(that.exec_) { : call_(that.call_), exec_(that.exec_) {
that.call_ = &Traits::uninitCall; that.call_ = &Traits::uninitCall;
...@@ -744,8 +736,22 @@ class Function final : private detail::function::FunctionTraits<FunctionType> { ...@@ -744,8 +736,22 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
std::enable_if_t<!detail::is_similar_instantiation_v<Function, Fun>>, std::enable_if_t<!detail::is_similar_instantiation_v<Function, Fun>>,
typename = typename Traits::template IfSafeResult<Fun>> typename = typename Traits::template IfSafeResult<Fun>>
/* implicit */ Function(Fun fun) noexcept( /* implicit */ Function(Fun fun) noexcept(
IsSmall<Fun>::value&& noexcept(Fun(std::declval<Fun>()))) IsSmall<Fun>::value&& noexcept(Fun(std::declval<Fun>()))) {
: Function(std::move(fun), IsSmall<Fun>{}) {} using Dispatch = conditional_t<
IsSmall<Fun>::value,
detail::function::DispatchSmall,
detail::function::DispatchBig>;
if (detail::function::isEmptyFunction(fun)) {
return;
}
if FOLLY_CXX17_CONSTEXPR (IsSmall<Fun>::value) {
::new (&data_.tiny) Fun(static_cast<Fun&&>(fun));
} else {
data_.big = new Fun(static_cast<Fun&&>(fun));
}
call_ = Dispatch::template call<Fun, Traits>;
exec_ = Dispatch::template exec<Fun>;
}
/** /**
* For move-constructing from a `folly::Function<X(Ys...) [const?]>`. * For move-constructing from a `folly::Function<X(Ys...) [const?]>`.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment