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