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