libcopp  2.2.0
sample_benchmark_std_couroutine_task_reuse_generator.cpp
Go to the documentation of this file.
1 // Copyright 2023 owent
2 // std coroutine 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 using benchmark_task_future_type = cotask::task_future<int64_t, void>;
35 using benchmark_generator_future_type = copp::generator_future<int64_t>;
36 
37 std::vector<benchmark_task_future_type> g_benchmark_task_list;
38 std::vector<benchmark_generator_future_type::context_pointer_type> g_benchmark_generator_list;
39 
40 int switch_count = 100;
41 int max_task_number = 100000;
42 
43 benchmark_task_future_type run_benchmark(size_t idx, int left_switch_count) {
44  int64_t result = 0;
45 
46  benchmark_generator_future_type generator{[idx](benchmark_generator_future_type::context_pointer_type ctx) {
47  g_benchmark_generator_list[idx] = std::move(ctx);
48  }};
49  while (left_switch_count-- >= 0) {
50  generator.get_context()->reset_value();
51  auto gen_res = co_await generator;
52  result += gen_res;
53  }
54 
55  co_return result;
56 }
57 
58 static void benchmark_round(int index) {
59  g_benchmark_task_list.reserve(static_cast<size_t>(max_task_number));
60  g_benchmark_generator_list.resize(static_cast<size_t>(max_task_number), nullptr);
61 
62  printf("### Round: %d ###\n", index);
63 
64  time_t begin_time = time(nullptr);
65  CALC_CLOCK_T begin_clock = CALC_CLOCK_NOW();
66 
67  // create coroutines task
68  while (g_benchmark_task_list.size() < static_cast<size_t>(max_task_number)) {
69  g_benchmark_task_list.push_back(run_benchmark(g_benchmark_task_list.size(), switch_count));
70  }
71  for (auto& task_inst : g_benchmark_task_list) {
72  task_inst.start();
73  }
74 
75  time_t end_time = time(nullptr);
76  CALC_CLOCK_T end_clock = CALC_CLOCK_NOW();
77  printf("create %d task(s) and generator(s), cost time: %d s, clock time: %d ms, avg: %lld ns\n", max_task_number,
78  static_cast<int>(end_time - begin_time), CALC_MS_CLOCK(end_clock - begin_clock),
79  CALC_NS_AVG_CLOCK(end_clock - begin_clock, max_task_number));
80 
81  begin_time = end_time;
82  begin_clock = end_clock;
83 
84  // yield & resume from runner
85  bool continue_flag = true;
86  long long real_switch_times = static_cast<long long>(0);
87  int32_t round = 0;
88 
89  while (continue_flag) {
90  ++round;
91  continue_flag = false;
92  for (auto& generator_context : g_benchmark_generator_list) {
93  benchmark_generator_future_type::context_pointer_type move_context;
94  move_context.swap(generator_context);
95  if (move_context) {
96  move_context->set_value(round);
97  ++real_switch_times;
98  continue_flag = true;
99  }
100  }
101  }
102 
103  end_time = time(nullptr);
104  end_clock = CALC_CLOCK_NOW();
105  printf("resume %d task(s) and generator(s) for %lld times, cost time: %d s, clock time: %d ms, avg: %lld ns\n",
106  max_task_number, real_switch_times, static_cast<int>(end_time - begin_time),
107  CALC_MS_CLOCK(end_clock - begin_clock), CALC_NS_AVG_CLOCK(end_clock - begin_clock, real_switch_times));
108 
109  begin_time = end_time;
110  begin_clock = end_clock;
111 
112  g_benchmark_task_list.clear();
113  g_benchmark_generator_list.clear();
114 
115  end_time = time(nullptr);
116  end_clock = CALC_CLOCK_NOW();
117  printf("remove %d task(s), cost time: %d s, clock time: %d ms, avg: %lld ns\n", max_task_number,
118  static_cast<int>(end_time - begin_time), CALC_MS_CLOCK(end_clock - begin_clock),
119  CALC_NS_AVG_CLOCK(end_clock - begin_clock, max_task_number));
120 }
121 
122 int main(int argc, char* argv[]) {
123  puts("###################### std task - reuse generator - trivial ###################");
124  printf("########## Cmd:");
125  for (int i = 0; i < argc; ++i) {
126  printf(" %s", argv[i]);
127  }
128  puts("");
129 
130  if (argc > 1) {
131  max_task_number = atoi(argv[1]);
132  }
133 
134  if (argc > 2) {
135  switch_count = atoi(argv[2]);
136  }
137 
138  for (int i = 1; i <= 5; ++i) {
139  benchmark_round(i);
140  }
141  return 0;
142 }
143 # else
144 int main() {
145  puts("task_future disabled.");
146  return 0;
147 }
148 # endif
149 #else
150 int main() {
151  puts("std coroutine is not supported by current compiler.");
152  return 0;
153 }
154 #endif
#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)