Summary: No reason to go through the whole Promise rigamarole. Add an appropriate Core ctor and use that to make a completed Future with just the core alloc
Note the big win in the `constantFuture` benchmark.
```
Before:
============================================================================
folly/futures/test/Benchmark.cpp relative time/iter iters/s
============================================================================
constantFuture 120.50ns 8.30M
promiseAndFuture 91.99% 130.98ns 7.63M
withThen 28.17% 427.77ns 2.34M
----------------------------------------------------------------------------
oneThen 430.48ns 2.32M
twoThens 58.03% 741.86ns 1.35M
fourThens 31.85% 1.35us 739.97K
hundredThens 1.61% 26.80us 37.32K
----------------------------------------------------------------------------
no_contention 4.58ms 218.48
contention 83.70% 5.47ms 182.86
----------------------------------------------------------------------------
throwAndCatch 8.09us 123.55K
throwAndCatchWrapped 94.43% 8.57us 116.67K
throwWrappedAndCatch 154.69% 5.23us 191.12K
throwWrappedAndCatchWrapped 614.06% 1.32us 758.70K
----------------------------------------------------------------------------
throwAndCatchContended 967.54ms 1.03
throwAndCatchWrappedContended 103.48% 935.04ms 1.07
throwWrappedAndCatchContended 148.24% 652.70ms 1.53
throwWrappedAndCatchWrappedContended 14313.28% 6.76ms 147.94
============================================================================
After:
============================================================================
folly/futures/test/Benchmark.cpp relative time/iter iters/s
============================================================================
constantFuture 69.11ns 14.47M
promiseAndFuture 55.12% 125.37ns 7.98M
withThen 16.49% 419.18ns 2.39M
----------------------------------------------------------------------------
oneThen 370.39ns 2.70M
twoThens 55.11% 672.05ns 1.49M
fourThens 29.00% 1.28us 782.89K
hundredThens 1.23% 30.22us 33.09K
----------------------------------------------------------------------------
no_contention 4.56ms 219.46
contention 82.82% 5.50ms 181.77
----------------------------------------------------------------------------
throwAndCatch 8.30us 120.42K
throwAndCatchWrapped 96.40% 8.61us 116.08K
throwWrappedAndCatch 162.66% 5.11us 195.89K
throwWrappedAndCatchWrapped 680.39% 1.22us 819.36K
----------------------------------------------------------------------------
throwAndCatchContended 979.17ms 1.02
throwAndCatchWrappedContended 103.09% 949.84ms 1.05
throwWrappedAndCatchContended 153.55% 637.71ms 1.57
throwWrappedAndCatchWrappedContended 10468.47% 9.35ms 106.91
============================================================================
```
Reviewed By: @fugalh, @​hannesr
Differential Revision:
D2144664
template <class T>
template <class T2, typename>
-Future<T>::Future(T2&& val) : core_(nullptr) {
- Promise<T> p;
- p.setValue(std::forward<T2>(val));
- *this = p.getFuture();
-}
+Future<T>::Future(T2&& val)
+ : core_(new detail::Core<T>(Try<T>(std::forward<T2>(val)))) {}
template <class T>
template <class T2,
typename std::enable_if<
folly::is_void_or_unit<T2>::value,
int>::type>
-Future<T>::Future() : core_(nullptr) {
- Promise<T> p;
- p.setValue();
- *this = p.getFuture();
-}
+Future<T>::Future()
+ : core_(new detail::Core<T>(Try<T>())) {}
template <class T>
template <class T>
Future<typename std::decay<T>::type> makeFuture(T&& t) {
- Promise<typename std::decay<T>::type> p;
- p.setValue(std::forward<T>(t));
- return p.getFuture();
+ return makeFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
}
inline // for multiple translation units
Future<void> makeFuture() {
- Promise<void> p;
- p.setValue();
- return p.getFuture();
+ return makeFuture(Try<void>());
}
template <class F>
F&& func,
typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
-> Future<decltype(func())> {
- Promise<decltype(func())> p;
- p.setWith(
- [&func]() {
- return (func)();
- });
- return p.getFuture();
+ return makeFuture(makeTryWith([&func]() {
+ return (func)();
+ }));
}
template <class F>
auto makeFutureWith(F const& func) -> Future<decltype(func())> {
F copy = func;
- return makeFutureWith(std::move(copy));
+ return makeFuture(makeTryWith(std::move(copy)));
}
template <class T>
Future<T> makeFuture(std::exception_ptr const& e) {
- Promise<T> p;
- p.setException(e);
- return p.getFuture();
+ return makeFuture(Try<T>(e));
}
template <class T>
Future<T> makeFuture(exception_wrapper ew) {
- Promise<T> p;
- p.setException(std::move(ew));
- return p.getFuture();
+ return makeFuture(Try<T>(std::move(ew)));
}
template <class T, class E>
typename std::enable_if<std::is_base_of<std::exception, E>::value,
Future<T>>::type
makeFuture(E const& e) {
- Promise<T> p;
- p.setException(make_exception_wrapper<E>(e));
- return p.getFuture();
+ return makeFuture(Try<T>(make_exception_wrapper<E>(e)));
}
template <class T>
Future<T> makeFuture(Try<T>&& t) {
- Promise<typename std::decay<T>::type> p;
- p.setTry(std::move(t));
- return p.getFuture();
-}
-
-template <>
-inline Future<void> makeFuture(Try<void>&& t) {
- if (t.hasException()) {
- return makeFuture<void>(std::move(t.exception()));
- } else {
- return makeFuture();
- }
+ return Future<T>(new detail::Core<T>(std::move(t)));
}
// via
friend class Promise<T>;
template <class> friend class Future;
+ template <class T2>
+ friend Future<T2> makeFuture(Try<T2>&&);
+
// Variant: returns a value
// e.g. f.then([](Try<T> t){ return t.value(); });
template <typename F, typename R, bool isTry, typename... Args>
/// code but since this is just internal detail code and I don't know how
/// off-hand, I'm punting.
Core() {}
+
+ explicit Core(Try<T>&& t)
+ : fsm_(State::OnlyResult),
+ attached_(1),
+ result_(std::move(t)) {}
+
~Core() {
assert(attached_ == 0);
}
makeFuture(42);
}
-// This shouldn't get too far below 100%
BENCHMARK_RELATIVE(promiseAndFuture) {
Promise<int> p;
Future<int> f = p.getFuture();
f.value();
}
-// The higher the better. At the time of writing, it's only about 40% :(
BENCHMARK_RELATIVE(withThen) {
Promise<int> p;
Future<int> f = p.getFuture().then(incr<int>);