libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
34using benchmark_task_future_type = cotask::task_future<int64_t, void>;
35using benchmark_generator_future_type = copp::generator_future<int64_t>;
36
37std::vector<benchmark_task_future_type> g_benchmark_task_list;
38std::vector<benchmark_generator_future_type::context_pointer_type> g_benchmark_generator_list;
39
40int switch_count = 100;
41int max_task_number = 100000;
42
43benchmark_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
58static 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
122int 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) {
140 }
141 return 0;
142}
143# else
144int main() {
145 puts("task_future disabled.");
146 return 0;
147}
148# endif
149#else
150int 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)