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