libcopp  2.2.0
sample_benchmark_std_couroutine_callable_create_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  while (left_switch_count-- >= 0) {
67  auto offset =
68  co_await benchmark_generator_future_type([idx](benchmark_generator_future_type::context_pointer_type ctx) {
69  g_benchmark_generator_list[idx] = std::move(ctx);
70  });
71  result += offset.data;
72  }
73 
74  co_return benchmark_no_trivial_message_t{result};
75 }
76 
77 int switch_count = 100;
78 int max_task_number = 100000;
79 
80 static void benchmark_round(int index) {
81  g_benchmark_callable_list.reserve(static_cast<size_t>(max_task_number));
82  g_benchmark_generator_list.resize(static_cast<size_t>(max_task_number), nullptr);
83 
84  printf("### Round: %d ###\n", index);
85 
86  time_t begin_time = time(nullptr);
87  CALC_CLOCK_T begin_clock = CALC_CLOCK_NOW();
88 
89  // create coroutines callable
90  while (g_benchmark_callable_list.size() < static_cast<size_t>(max_task_number)) {
91  g_benchmark_callable_list.push_back(std::unique_ptr<benchmark_callable_future_type>(
92  new benchmark_callable_future_type(run_benchmark(g_benchmark_callable_list.size(), switch_count))));
93  }
94 
95  time_t end_time = time(nullptr);
96  CALC_CLOCK_T end_clock = CALC_CLOCK_NOW();
97  printf("create %d callable(s) and generator(s), cost time: %d s, clock time: %d ms, avg: %lld ns\n", max_task_number,
98  static_cast<int>(end_time - begin_time), CALC_MS_CLOCK(end_clock - begin_clock),
99  CALC_NS_AVG_CLOCK(end_clock - begin_clock, max_task_number));
100 
101  begin_time = end_time;
102  begin_clock = end_clock;
103 
104  // yield & resume from runner
105  bool continue_flag = true;
106  long long real_switch_times = static_cast<long long>(0);
107  int32_t round = 0;
108 
109  while (continue_flag) {
110  ++round;
111  continue_flag = false;
112  for (auto &generator_context : g_benchmark_generator_list) {
113  benchmark_generator_future_type::context_pointer_type move_context;
114  move_context.swap(generator_context);
115  if (move_context) {
116  move_context->set_value(benchmark_no_trivial_message_t{round});
117  ++real_switch_times;
118  continue_flag = true;
119  }
120  }
121  }
122 
123  end_time = time(nullptr);
124  end_clock = CALC_CLOCK_NOW();
125  printf("resume %d callable(s) and generator(s) for %lld times, cost time: %d s, clock time: %d ms, avg: %lld ns\n",
126  max_task_number, real_switch_times, static_cast<int>(end_time - begin_time),
127  CALC_MS_CLOCK(end_clock - begin_clock), CALC_NS_AVG_CLOCK(end_clock - begin_clock, real_switch_times));
128 
129  begin_time = end_time;
130  begin_clock = end_clock;
131 
132  g_benchmark_callable_list.clear();
133  g_benchmark_generator_list.clear();
134 
135  end_time = time(nullptr);
136  end_clock = CALC_CLOCK_NOW();
137  printf("remove %d callable(s), cost time: %d s, clock time: %d ms, avg: %lld ns\n", max_task_number,
138  static_cast<int>(end_time - begin_time), CALC_MS_CLOCK(end_clock - begin_clock),
139  CALC_NS_AVG_CLOCK(end_clock - begin_clock, max_task_number));
140 }
141 
142 int main(int argc, char *argv[]) {
143  puts("###################### std callable - create generator - no trivial ###################");
144  printf("########## Cmd:");
145  for (int i = 0; i < argc; ++i) {
146  printf(" %s", argv[i]);
147  }
148  puts("");
149 
150  if (argc > 1) {
151  max_task_number = atoi(argv[1]);
152  }
153 
154  if (argc > 2) {
155  switch_count = atoi(argv[2]);
156  }
157 
158  for (int i = 1; i <= 5; ++i) {
159  benchmark_round(i);
160  }
161  return 0;
162 }
163 #else
164 int main() {
165  puts("std coroutine is not supported by current compiler.");
166  return 0;
167 }
168 #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)