optimize makeFuture and Future<T>::Future()
authorJames Sedgwick <jsedgwick@fb.com>
Wed, 10 Jun 2015 23:29:08 +0000 (16:29 -0700)
committerSara Golemon <sgolemon@fb.com>
Thu, 11 Jun 2015 20:15:12 +0000 (13:15 -0700)
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

folly/futures/Future-inl.h
folly/futures/Future.h
folly/futures/detail/Core.h
folly/futures/test/Benchmark.cpp

index 4b2c7477675cb22c94c4ef3a530b84f603dbf28b..0b2a301c5014c134f5d5ebed226971df79d7aa7e 100644 (file)
@@ -45,22 +45,16 @@ Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
 
 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>
@@ -456,16 +450,12 @@ void Future<T>::raise(exception_wrapper exception) {
 
 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>
@@ -473,57 +463,37 @@ auto makeFutureWith(
     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
index 4f5db68e011b80db1fd7e54af15859e97ec532af..40687b42b4f5cc7deb7dabcf7ace69105ad42e91 100644 (file)
@@ -432,6 +432,9 @@ class Future {
   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>
index de0173e78b8fac3061cadffd9ff1ab8a8a5bd911..27842b55d2e92062359b341d1dea5f8ff6ded81f 100644 (file)
@@ -79,6 +79,12 @@ class Core {
   /// 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);
   }
index 54771b1ed76584d2cae55d8aab8049d933ebf7b9..386e8b9d02f7d9e51cfcf2aef3196095c1caeee2 100644 (file)
@@ -46,7 +46,6 @@ BENCHMARK(constantFuture) {
   makeFuture(42);
 }
 
-// This shouldn't get too far below 100%
 BENCHMARK_RELATIVE(promiseAndFuture) {
   Promise<int> p;
   Future<int> f = p.getFuture();
@@ -54,7 +53,6 @@ BENCHMARK_RELATIVE(promiseAndFuture) {
   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>);