Commit 9ee00d31 authored by Sven Over's avatar Sven Over Committed by Facebook Github Bot

Fast-path destructor

Summary:
Optimise the destruction of default-constructed and moved-out Function objects. Instead of calling a function pointer that is set to a no-op function, use `nullptr` to signal emptyness of that function pointer and check if it is `nullptr` before invoking.
This adds additional branching in a few places: not only in the destructor, but also in the move constructor, as well as in the little used `hasAllocatedMemory`. The effect of getting rid of calls to a no-op function pointer should outweigh that.

Reviewed By: djwatson

Differential Revision: D8694003

fbshipit-source-id: 42972805b2ade255cf1dcc98e54985b1524daa73
parent 73a507a2
......@@ -247,7 +247,7 @@ Function<ReturnType(Args...) const noexcept> constCastFunction(
namespace detail {
namespace function {
enum class Op { MOVE, NUKE, FULL, HEAP };
enum class Op { MOVE, NUKE, HEAP };
union Data {
Data() {}
......@@ -282,10 +282,6 @@ std::false_type isNullPtrFn(T&&) {
return {};
}
inline bool uninitNoop(Op, Data*, Data*) {
return false;
}
template <typename F, typename... Args>
using CallableResult = decltype(std::declval<F>()(std::declval<Args>()...));
......@@ -493,8 +489,6 @@ bool execSmall(Op o, Data* src, Data* dst) {
case Op::NUKE:
static_cast<Fun*>(static_cast<void*>(&src->tiny))->~Fun();
break;
case Op::FULL:
return true;
case Op::HEAP:
break;
}
......@@ -511,7 +505,6 @@ bool execBig(Op o, Data* src, Data* dst) {
case Op::NUKE:
delete static_cast<Fun*>(src->big);
break;
case Op::FULL:
case Op::HEAP:
break;
}
......@@ -545,7 +538,11 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
// is the result of calling `constCastFunction`.
mutable Data data_{};
Call call_{&Traits::uninitCall};
Exec exec_{&detail::function::uninitNoop};
Exec exec_{nullptr};
bool exec(Op o, Data* src, Data* dst) const {
return exec_ && exec_(o, src, dst);
}
friend Traits;
friend Function<typename Traits::ConstSignature> folly::constCastFunction<>(
......@@ -577,7 +574,7 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
Function(
Function<typename Traits::OtherSignature>&& that,
CoerceTag) noexcept {
that.exec_(Op::MOVE, &that.data_, &data_);
that.exec(Op::MOVE, &that.data_, &data_);
std::swap(call_, that.call_);
std::swap(exec_, that.exec_);
}
......@@ -603,7 +600,7 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
* Move constructor
*/
Function(Function&& that) noexcept {
that.exec_(Op::MOVE, &that.data_, &data_);
that.exec(Op::MOVE, &that.data_, &data_);
std::swap(call_, that.call_);
std::swap(exec_, that.exec_);
}
......@@ -674,7 +671,7 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
}
~Function() {
exec_(Op::NUKE, &data_, nullptr);
exec(Op::NUKE, &data_, nullptr);
}
Function& operator=(const Function&) = delete;
......@@ -782,7 +779,7 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
* non-empty.
*/
explicit operator bool() const noexcept {
return exec_(Op::FULL, nullptr, nullptr);
return exec_ != nullptr;
}
/**
......@@ -792,7 +789,7 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
* object itself.
*/
bool hasAllocatedMemory() const noexcept {
return exec_(Op::HEAP, nullptr, nullptr);
return exec(Op::HEAP, nullptr, nullptr);
}
using typename Traits::SharedProxy;
......
......@@ -417,54 +417,54 @@ int main(int argc, char** argv) {
============================================================================
folly/test/function_benchmark/main.cpp relative time/iter iters/s
============================================================================
fn_invoke 1.02ns 980.05M
fn_ptr_invoke 1.22ns 822.98M
std_function_invoke 2.73ns 365.78M
Function_invoke 2.73ns 365.80M
mem_fn_invoke 1.37ns 731.32M
fn_ptr_invoke_through_inline 1.22ns 822.98M
lambda_invoke_fn 1.22ns 822.96M
fn_invoke 1.21ns 825.86M
fn_ptr_invoke 1.24ns 809.24M
std_function_invoke 2.76ns 362.09M
Function_invoke 2.75ns 364.02M
mem_fn_invoke 1.22ns 821.10M
fn_ptr_invoke_through_inline 1.21ns 826.38M
lambda_invoke_fn 1.22ns 821.00M
lambda_noop 0.00fs Infinity
lambda_local_var 182.45ps 5.48G
fn_ptr_invoke_through_template 911.58ps 1.10G
virtual_fn_invoke 1.22ns 822.99M
fn_ptr_create_invoke 1.22ns 822.97M
std_function_create_invoke 3.92ns 255.32M
Function_create_invoke 2.84ns 351.67M
mem_fn_create_invoke 1.22ns 822.99M
std_bind_create_invoke 18.92ns 52.85M
std_bind_direct_invoke 1.22ns 822.99M
scope_guard_std_function 7.36ns 135.87M
scope_guard_std_function_rvalue 6.46ns 154.70M
scope_guard_Function_rvalue 4.94ns 202.59M
scope_guard_fn_ptr 1.22ns 822.97M
lambda_local_var 195.77ps 5.11G
fn_ptr_invoke_through_template 1.22ns 819.76M
virtual_fn_invoke 1.21ns 826.17M
fn_ptr_create_invoke 1.21ns 826.90M
std_function_create_invoke 3.72ns 268.86M
Function_create_invoke 2.80ns 357.04M
mem_fn_create_invoke 1.21ns 826.38M
std_bind_create_invoke 19.03ns 52.55M
std_bind_direct_invoke 1.21ns 824.20M
scope_guard_std_function 7.28ns 137.42M
scope_guard_std_function_rvalue 6.60ns 151.52M
scope_guard_Function_rvalue 4.93ns 202.65M
scope_guard_fn_ptr 1.23ns 815.68M
scope_guard_lambda_noop 0.00fs Infinity
scope_guard_lambda_function 1.22ns 822.99M
scope_guard_lambda_local_var 101.27ps 9.87G
scope_guard_lambda_function 1.24ns 807.24M
scope_guard_lambda_local_var 89.99ps 11.11G
----------------------------------------------------------------------------
throw_exception 1.88us 531.42K
catch_no_exception 1.22ns 822.98M
return_exc_ptr 1.39us 717.63K
exc_ptr_param_return 1.40us 712.23K
exc_ptr_param_return_null 1.22ns 823.00M
return_string 2.43ns 411.48M
return_string_noexcept 2.43ns 411.48M
return_code 961.88ps 1.04G
return_code_noexcept 1.22ns 822.99M
throw_exception 1.91us 523.99K
catch_no_exception 1.22ns 820.79M
return_exc_ptr 1.40us 713.05K
exc_ptr_param_return 1.42us 704.62K
exc_ptr_param_return_null 1.21ns 826.90M
return_string 2.75ns 364.14M
return_string_noexcept 2.74ns 365.15M
return_code 903.47ps 1.11G
return_code_noexcept 1.21ns 823.26M
----------------------------------------------------------------------------
std_function_create_move_invoke 47.37ns 21.11M
Function_create_move_invoke 53.55ns 18.67M
std_function_create_move_invoke_small 6.76ns 147.82M
Function_create_move_invoke_small 7.75ns 129.08M
std_function_create_move_invoke_ref 6.67ns 150.02M
Function_create_move_invoke_ref 7.63ns 131.09M
std_function_create_move_invoke 53.45ns 18.71M
Function_create_move_invoke 50.59ns 19.77M
std_function_create_move_invoke_small 6.89ns 145.19M
Function_create_move_invoke_small 7.04ns 141.96M
std_function_create_move_invoke_ref 6.74ns 148.43M
Function_create_move_invoke_ref 6.93ns 144.39M
----------------------------------------------------------------------------
function_ptr_move 1.21ns 823.06M
std_function_move_small 5.77ns 173.20M
Function_move_small 10.17ns 98.31M
std_function_move_small_trivial 5.77ns 173.27M
Function_move_small_trivial 8.78ns 113.85M
std_function_move_large 5.77ns 173.23M
Function_move_large 8.21ns 121.85M
function_ptr_move 1.21ns 825.65M
std_function_move_small 5.82ns 171.96M
Function_move_small 8.34ns 119.90M
std_function_move_small_trivial 5.79ns 172.70M
Function_move_small_trivial 6.68ns 149.72M
std_function_move_large 5.79ns 172.59M
Function_move_large 6.00ns 166.78M
============================================================================
*/
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