libcopp  2.2.0
sample_benchmark_std_couroutine_callable_reuse_generator_no_trivial.cpp
Go to the documentation of this file.
1 // Copyright 2023 owent
2 // std coroutine no trivial callable benchmark
3 
6 
7 #include <inttypes.h>
8 #include <stdint.h>
9 #include <cstdio>
10 #include <cstdlib>
11 #include <cstring>
12 #include <ctime>
13 #include <vector>
14 
15 #if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
16 
17 # if defined(PROJECT_LIBCOPP_SAMPLE_HAS_CHRONO) && PROJECT_LIBCOPP_SAMPLE_HAS_CHRONO
18 # include <chrono>
19 # define CALC_CLOCK_T std::chrono::system_clock::time_point
20 # define CALC_CLOCK_NOW() std::chrono::system_clock::now()
21 # define CALC_MS_CLOCK(x) static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(x).count())
22 # define CALC_NS_AVG_CLOCK(x, y) \
23  static_cast<long long>(std::chrono::duration_cast<std::chrono::nanoseconds>(x).count() / (y ? y : 1))
24 # else
25 # define CALC_CLOCK_T clock_t
26 # define CALC_CLOCK_NOW() clock()
27 # define CALC_MS_CLOCK(x) static_cast<int>((x) / (CLOCKS_PER_SEC / 1000))
28 # define CALC_NS_AVG_CLOCK(x, y) (1000000LL * static_cast<long long>((x) / (CLOCKS_PER_SEC / 1000)) / (y ? y : 1))
29 # endif
30 
31 struct benchmark_no_trivial_message_t {
32  ~benchmark_no_trivial_message_t() {}
33 
34  // For callable
35  benchmark_no_trivial_message_t() : data(0) {}
36  template <class TFirst, class... TRest, typename = std::enable_if_t<!std::is_same_v<std::decay_t<TFirst>, int64_t>>>
37  benchmark_no_trivial_message_t(TFirst &&, TRest &&...) : data(0) {}
38 
39  // For generator
40  benchmark_no_trivial_message_t(int64_t input) : data(input) {}
41  benchmark_no_trivial_message_t(const benchmark_no_trivial_message_t &) = default;
42  benchmark_no_trivial_message_t(benchmark_no_trivial_message_t &&) = default;
43  benchmark_no_trivial_message_t &operator=(const benchmark_no_trivial_message_t &) = default;
44  benchmark_no_trivial_message_t &operator=(benchmark_no_trivial_message_t &&) = default;
45 
46  int64_t data;
47 };
48 
49 LIBCOPP_COPP_NAMESPACE_BEGIN
50 template <>
51 struct std_coroutine_default_error_transform<benchmark_no_trivial_message_t> {
52  using type = benchmark_no_trivial_message_t;
53  type operator()(promise_status in) const { return benchmark_no_trivial_message_t{static_cast<int64_t>(in)}; }
54 };
55 LIBCOPP_COPP_NAMESPACE_END
56 
57 using benchmark_callable_future_type = copp::callable_future<benchmark_no_trivial_message_t>;
58 using benchmark_generator_future_type = copp::generator_future<benchmark_no_trivial_message_t>;
59 
60 std::vector<std::unique_ptr<benchmark_callable_future_type>> g_benchmark_callable_list;
61 std::vector<benchmark_generator_future_type::context_pointer_type> g_benchmark_generator_list;
62 
63 benchmark_callable_future_type run_benchmark(size_t idx, int left_switch_count) {
64  int64_t result = 0;
65 
66  benchmark_generator_future_type generator{[idx](benchmark_generator_future_type::context_pointer_type ctx) {
67  g_benchmark_generator_list[idx] = std::move(ctx);
68  }};
69  while (left_switch_count-- >= 0) {
70  generator.get_context()->reset_value();
71  auto offset = co_await generator;
72  result += offset.data;
73  }
74 
75  co_return benchmark_no_trivial_message_t{result};
76 }
77 
78 int switch_count = 100;
79 int max_task_number = 100000;
80 
81 static void benchmark_round(int index) {
82  g_benchmark_callable_list.reserve(static_cast<size_t>(max_task_number));
83  g_benchmark_generator_list.resize(static_cast<size_t>(max_task_number), nullptr);
84 
85  printf("### Round: %d ###\n", index);
86 
87  time_t begin_time = time(nullptr);
88  CALC_CLOCK_T begin_clock = CALC_CLOCK_NOW();
89 
90  // create coroutines callable
91  while (g_benchmark_callable_list.size() < static_cast<size_t>(max_task_number)) {
92  g_benchmark_callable_list.push_back(std::unique_ptr<benchmark_callable_future_type>(
93  new benchmark_callable_future_type(run_benchmark(g_benchmark_callable_list.size(), switch_count))));
94  }
95 
96  time_t end_time = time(nullptr);
97  CALC_CLOCK_T end_clock = CALC_CLOCK_NOW();
98  printf("create %d callable(s) and generator(s), cost time: %d s, clock time: %d ms, avg: %lld ns\n", max_task_number,
99  static_cast<int>(end_time - begin_time), CALC_MS_CLOCK(end_clock - begin_clock),
100  CALC_NS_AVG_CLOCK(end_clock - begin_clock, max_task_number));
101 
102  begin_time = end_time;
103  begin_clock = end_clock;
104 
105  // yield & resume from runner
106  bool continue_flag = true;
107  long long real_switch_times = static_cast<long long>(0);
108  int32_t round = 0;
109 
110  while (continue_flag) {
111  ++round;
112  continue_flag = false;
113  for (auto &generator_context : g_benchmark_generator_list) {
114  benchmark_generator_future_type::context_pointer_type move_context;
115  move_context.swap(generator_context);
116  if (move_context) {
117  move_context->set_value(benchmark_no_trivial_message_t{round});
118  ++real_switch_times;
119  continue_flag = true;
120  }
121  }
122  }
123 
124  end_time = time(nullptr);
125  end_clock = CALC_CLOCK_NOW();
126  printf("resume %d callable(s) and generator(s) for %lld times, cost time: %d s, clock time: %d ms, avg: %lld ns\n",
127  max_task_number, real_switch_times, static_cast<int>(end_time - begin_time),
128  CALC_MS_CLOCK(end_clock - begin_clock), CALC_NS_AVG_CLOCK(end_clock - begin_clock, real_switch_times));
129 
130  begin_time = end_time;
131  begin_clock = end_clock;
132 
133  g_benchmark_callable_list.clear();
134  g_benchmark_generator_list.clear();
135 
136  end_time = time(nullptr);
137  end_clock = CALC_CLOCK_NOW();
138  printf("remove %d callable(s), cost time: %d s, clock time: %d ms, avg: %lld ns\n", max_task_number,
139  static_cast<int>(end_time - begin_time), CALC_MS_CLOCK(end_clock - begin_clock),
140  CALC_NS_AVG_CLOCK(end_clock - begin_clock, max_task_number));
141 }
142 
143 int main(int argc, char *argv[]) {
144  puts("###################### std callable - reuse generator - no trivial ###################");
145  printf("########## Cmd:");
146  for (int i = 0; i < argc; ++i) {
147  printf(" %s", argv[i]);
148  }
149  puts("");
150 
151  if (argc > 1) {
152  max_task_number = atoi(argv[1]);
153  }
154 
155  if (argc > 2) {
156  switch_count = atoi(argv[2]);
157  }
158 
159  for (int i = 1; i <= 5; ++i) {
160  benchmark_round(i);
161  }
162  return 0;
163 }
164 #else
165 int main() {
166  puts("std coroutine is not supported by current compiler.");
167  return 0;
168 }
169 #endif
constexpr auto data(TCONTAINER &&container) -> decltype(container.data())
Definition: span.h:54
#define CALC_CLOCK_T
#define CALC_MS_CLOCK(x)
#define CALC_NS_AVG_CLOCK(x, y)
#define CALC_CLOCK_NOW()
static void benchmark_round(int index)