proxygen
CoroBenchmarkNRVO.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/Benchmark.h>
18 #include <thread>
19 
20 struct ExpensiveCopy {
22 
24  std::this_thread::sleep_for(std::chrono::milliseconds{1});
25  }
26 };
27 
28 #if FOLLY_HAS_COROUTINES
29 #include <experimental/coroutine>
30 #include <future>
31 
32 class Wait {
33  public:
34  class promise_type {
35  public:
36  Wait get_return_object() {
37  return Wait(promise_.get_future());
38  }
39 
40  std::experimental::suspend_never initial_suspend() {
41  return {};
42  }
43 
44  std::experimental::suspend_never final_suspend() {
45  return {};
46  }
47 
48  void return_void() {
49  promise_.set_value();
50  }
51 
52  void unhandled_exception() {
53  promise_.set_exception(std::current_exception());
54  }
55 
56  private:
57  std::promise<void> promise_;
58  };
59 
60  explicit Wait(std::future<void> future) : future_(std::move(future)) {}
61 
62  Wait(Wait&&) = default;
63 
64  void detach() {
65  future_ = {};
66  }
67 
68  ~Wait() {
69  if (future_.valid()) {
70  future_.get();
71  }
72  }
73 
74  private:
75  std::future<void> future_;
76 };
77 
78 template <typename T>
79 class InlineTask {
80  public:
81  InlineTask(const InlineTask&) = delete;
82  InlineTask(InlineTask&& other)
83  : promise_(std::exchange(other.promise_, nullptr)) {}
84 
85  ~InlineTask() {
86  DCHECK(!promise_);
87  }
88 
89  bool await_ready() const {
90  return false;
91  }
92 
93  std::experimental::coroutine_handle<> await_suspend(
94  std::experimental::coroutine_handle<> awaiter) {
95  promise_->valuePtr_ = &value_;
96  promise_->awaiter_ = std::move(awaiter);
97  return std::experimental::coroutine_handle<promise_type>::from_promise(
98  *promise_);
99  }
100 
101  T await_resume() {
102  std::experimental::coroutine_handle<promise_type>::from_promise(
103  *std::exchange(promise_, nullptr))
104  .destroy();
105  T value = std::move(value_);
106  return value;
107  }
108 
109  class promise_type {
110  public:
111  InlineTask get_return_object() {
112  return InlineTask(this);
113  }
114 
115  template <typename U>
116  void return_value(U&& value) {
117  *valuePtr_ = std::forward<U>(value);
118  }
119 
120  void unhandled_exception() {
121  std::terminate();
122  }
123 
124  std::experimental::suspend_always initial_suspend() {
125  return {};
126  }
127 
128  class FinalSuspender {
129  public:
130  explicit FinalSuspender(std::experimental::coroutine_handle<> awaiter)
131  : awaiter_(std::move(awaiter)) {}
132 
133  bool await_ready() {
134  return false;
135  }
136 
137  auto await_suspend(std::experimental::coroutine_handle<>) {
138  return awaiter_;
139  }
140 
141  void await_resume() {}
142 
143  private:
144  std::experimental::coroutine_handle<> awaiter_;
145  };
146 
147  FinalSuspender final_suspend() {
148  return FinalSuspender(std::move(awaiter_));
149  }
150 
151  private:
152  friend class InlineTask;
153 
154  T* valuePtr_;
155  std::experimental::coroutine_handle<> awaiter_;
156  };
157 
158  private:
159  friend class promise_type;
160 
161  explicit InlineTask(promise_type* promise) : promise_(promise) {}
162 
163  T value_;
164  promise_type* promise_;
165 };
166 
167 InlineTask<ExpensiveCopy> co_nestedCalls(size_t times) {
168  ExpensiveCopy ret;
169  if (times > 0) {
170  ret = co_await co_nestedCalls(times - 1);
171  }
172  co_return ret;
173 }
174 
175 void coroNRVO(size_t times, size_t iters) {
176  for (size_t iter = 0; iter < iters; ++iter) {
177  [](size_t times) -> Wait {
178  (void)co_await co_nestedCalls(times);
179  co_return;
180  }(times);
181  }
182 }
183 
184 BENCHMARK(coroNRVOOneAwait, iters) {
185  coroNRVO(1, iters);
186 }
187 
188 BENCHMARK(coroNRVOTenAwaits, iters) {
189  coroNRVO(10, iters);
190 }
191 #endif
192 
194  ExpensiveCopy ret;
195  if (times > 0) {
196  ret = nestedCalls(times - 1);
197  }
198 
199  return ret;
200 }
201 
202 void NRVO(size_t times, size_t iters) {
203  for (size_t iter = 0; iter < iters; ++iter) {
204  nestedCalls(times);
205  }
206 }
207 
208 BENCHMARK(NRVOOneAwait, iters) {
209  NRVO(1, iters);
210 }
211 
212 BENCHMARK(NRVOTenAwaits, iters) {
213  NRVO(10, iters);
214 }
215 
216 int main(int argc, char** argv) {
217  gflags::ParseCommandLineFlags(&argc, &argv, true);
219  return 0;
220 }
#define T(v)
Definition: http_parser.c:233
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
BENCHMARK(NRVOOneAwait, iters)
void runBenchmarks()
Definition: Benchmark.cpp:456
char ** argv
ExpensiveCopy(const ExpensiveCopy &)
int main(int argc, char **argv)
Promise< Unit > promise_
static const char *const value
Definition: Conv.cpp:50
void NRVO(size_t times, size_t iters)
T exchange(T &obj, U &&new_value)
Definition: Utility.h:120
ExpensiveCopy nestedCalls(size_t times)
Future< Unit > times(const int n, F &&thunk)
Definition: Future-inl.h:2348