1+ #include < algorithm>
2+ #include < atomic>
3+ #include < chrono>
4+ #include < future>
5+ #include < iostream>
6+ #include < mutex>
7+ #include < numeric>
8+ #include < random>
9+ #include < ranges>
10+ #include < thread>
11+ #include < vector>
12+
13+ // Sequential
14+ int in_seq{0 }, total_seq{0 };
15+ // Atomic
16+ std::atomic<int > in_atomic_1{0 }, total_atomic_1{0 };
17+ std::atomic<int > in_atomic_2{0 }, total_atomic_2{0 };
18+ // Mutex
19+ int in_mutex_1{0 }, total_mutex_1{0 };
20+ int in_mutex_2{0 }, total_mutex_2{0 };
21+ std::mutex mtx;
22+
23+ struct vector2f
24+ {
25+ float x, y;
26+ };
27+
28+ vector2f get_random_point ()
29+ {
30+ thread_local std::random_device rd;
31+ thread_local std::default_random_engine eng (rd ());
32+ thread_local std::uniform_real_distribution<float > distr (-1 , 1 );
33+ return {distr (eng), distr (eng)};
34+ }
35+
36+ void count_points_atomic_1 (int number_of_points);
37+ void count_points_atomic_2 (int start, int end);
38+
39+ void count_points_mutex_1 (int number_of_points);
40+ void count_points_mutex_2 (int start, int end);
41+
42+ int count_points_future_1 (int num_of_points);
43+ int count_points_future_2 (int start, int end);
44+
45+ void count_points (int number)
46+ {
47+ total_seq += number;
48+ for (int i{}; i < number; ++i)
49+ {
50+ const auto point{get_random_point ()};
51+ if (point.x * point.x + point.y * point.y < 1 .f )
52+ {
53+ ++in_seq;
54+ }
55+ }
56+ }
57+
58+ void count_points_atomic_1 (int number_of_points)
59+ {
60+ int in = 0 ;
61+ for (int i = 0 ; i < number_of_points; ++i)
62+ {
63+ const auto point = get_random_point ();
64+ if (point.x * point.x + point.y * point.y < 1 .f )
65+ {
66+ ++in;
67+ }
68+ }
69+ in_atomic_1 += in;
70+ total_atomic_1 += number_of_points;
71+ }
72+
73+ void count_points_atomic_2 (int start, int end)
74+ {
75+ int in = 0 ;
76+ for (int i = start; i < end; ++i)
77+ {
78+ const auto point = get_random_point ();
79+ if (point.x * point.x + point.y * point.y < 1 .f )
80+ {
81+ ++in;
82+ }
83+ }
84+ in_atomic_2 += in;
85+ total_atomic_2 += end - start;
86+ }
87+
88+ void count_points_mutex_1 (int number_of_points)
89+ {
90+ int in = 0 ;
91+ for (int i = 0 ; i < number_of_points; ++i)
92+ {
93+ const auto point = get_random_point ();
94+ if (point.x * point.x + point.y * point.y < 1 .f )
95+ {
96+ ++in;
97+ }
98+ }
99+ std::lock_guard lock (mtx);
100+ in_mutex_1 += in;
101+ total_mutex_1 += number_of_points;
102+ }
103+
104+ void count_points_mutex_2 (int start, int end)
105+ {
106+ int in = 0 ;
107+ for (int i = start; i < end; ++i)
108+ {
109+ const auto point = get_random_point ();
110+ if (point.x * point.x + point.y * point.y < 1 .f )
111+ {
112+ ++in;
113+ }
114+ }
115+ std::lock_guard lock (mtx);
116+ in_mutex_2 += in;
117+ total_mutex_2 += end - start;
118+ }
119+
120+ int count_points_future_1 (int num_of_points)
121+ {
122+ int in = 0 ;
123+ for (int i = 0 ; i < num_of_points; ++i)
124+ {
125+ const auto point = get_random_point ();
126+ if (point.x * point.x + point.y * point.y < 1 .f )
127+ {
128+ ++in;
129+ }
130+ }
131+ return in;
132+ }
133+
134+ int count_points_future_2 (int start, int end)
135+ {
136+ int in = 0 ;
137+ for (int i = start; i < end; ++i)
138+ {
139+ const auto point = get_random_point ();
140+ if (point.x * point.x + point.y * point.y < 1 .f )
141+ {
142+ ++in;
143+ }
144+ }
145+ return in;
146+ }
147+
148+ int main ()
149+ {
150+ std::vector<long long > measured_times;
151+
152+ // const int numThreads = std::thread::hardware_concurrency();
153+ constexpr int num_threads = 10 ;
154+ constexpr int total_points = 100'000'000 ;
155+ constexpr int points_per_thread = total_points / num_threads;
156+
157+ // --------------------------------------------------------------------------------
158+ // Sequential
159+ // --------------------------------------------------------------------------------
160+ for (int i = 0 ; i < 10 ; ++i)
161+ {
162+ auto start_time = std::chrono::high_resolution_clock::now ();
163+ count_points (total_points);
164+ auto end_time = std::chrono::high_resolution_clock::now ();
165+ measured_times.push_back (std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count ());
166+ }
167+ auto [min1, max1] = std::ranges::minmax_element (measured_times);
168+ auto average = (std::accumulate (measured_times.begin (), measured_times.end (), 0LL ) - (*min1 + *max1)) / (measured_times.size () - 2 );
169+ std::cout << " Average time (sequence): " << average << " ms\n " ;
170+ std::cout << " Pi accuracy: " << 1 .f * in_seq / total_seq * 4 << " \n " ;
171+ std::cout << " In: " << in_seq << " Total: " << total_seq << " \n\n " ;
172+
173+ measured_times.clear ();
174+
175+ // --------------------------------------------------------------------------------
176+ // Atomic
177+ // --------------------------------------------------------------------------------
178+ for (int i = 0 ; i < 10 ; ++i)
179+ {
180+ auto start_time = std::chrono::high_resolution_clock::now ();
181+ {
182+ std::vector<std::jthread> threads (10 );
183+ for (int j = 0 ; j < num_threads; ++j)
184+ {
185+ threads.emplace_back (count_points_atomic_1, points_per_thread);
186+ }
187+ }
188+ auto end_time = std::chrono::high_resolution_clock::now ();
189+ measured_times.push_back (std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count ());
190+ }
191+ auto [min2, max2] = std::ranges::minmax_element (measured_times);
192+ average = (std::accumulate (measured_times.begin (), measured_times.end (), 0LL ) - (*min2 + *max2)) / (measured_times.size () - 2 );
193+ std::cout << " Average time (atomic) - number_of_points: " << average << " ms\n " ;
194+ std::cout << " Pi accuracy: " << 1 .f * in_atomic_1 / total_atomic_1 * 4 << " \n " ;
195+ std::cout << " In: " << in_atomic_1 << " Total: " << total_atomic_1 << " \n\n " ;
196+
197+ measured_times.clear ();
198+
199+ for (int i = 0 ; i < 10 ; ++i)
200+ {
201+ auto start_time = std::chrono::high_resolution_clock::now ();
202+ {
203+ std::vector<std::jthread> threads (10 );
204+ for (int j = 0 ; j < num_threads; ++j)
205+ {
206+ int start = j * points_per_thread;
207+ int end = (j == num_threads - 1 ) ? total_points : (j + 1 ) * points_per_thread;
208+ threads.emplace_back (count_points_atomic_2, start, end);
209+ }
210+ }
211+ auto end_time = std::chrono::high_resolution_clock::now ();
212+ measured_times.push_back (std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count ());
213+ }
214+ auto [min3, max3] = std::ranges::minmax_element (measured_times);
215+ average = (std::accumulate (measured_times.begin (), measured_times.end (), 0LL ) - (*min3 + *max3)) / (measured_times.size () - 2 );
216+ std::cout << " Average time (atomic) - start, end: " << average << " ms\n " ;
217+ std::cout << " Pi accuracy: " << 1 .f * in_atomic_2 / total_atomic_2 * 4 << " \n " ;
218+ std::cout << " In: " << in_atomic_2 << " Total: " << total_atomic_2 << " \n\n " ;
219+
220+ measured_times.clear ();
221+
222+ // --------------------------------------------------------------------------------
223+ // Mutex
224+ // --------------------------------------------------------------------------------
225+ for (int i = 0 ; i < 10 ; ++i)
226+ {
227+ auto start_time = std::chrono::high_resolution_clock::now ();
228+ {
229+ std::vector<std::jthread> threads (10 );
230+ for (int j = 0 ; j < num_threads; ++j)
231+ {
232+ threads.emplace_back (count_points_mutex_1, points_per_thread);
233+ }
234+ }
235+ auto end_time = std::chrono::high_resolution_clock::now ();
236+ measured_times.push_back (std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count ());
237+ }
238+ auto [min4, max4] = std::ranges::minmax_element (measured_times);
239+ average = (std::accumulate (measured_times.begin (), measured_times.end (), 0LL ) - (*min4 + *max4)) / (measured_times.size () - 2 );
240+ std::cout << " Average time (mutex) - number_of_points: " << average << " ms\n " ;
241+ std::cout << " Pi accuracy: " << 1 .f * in_mutex_1 / total_mutex_1 * 4 << " \n " ;
242+ std::cout << " In: " << in_mutex_1 << " Total: " << total_mutex_1 << " \n\n " ;
243+
244+ measured_times.clear ();
245+
246+ for (int i = 0 ; i < 10 ; ++i)
247+ {
248+ auto start_time = std::chrono::high_resolution_clock::now ();
249+ {
250+ std::vector<std::jthread> threads (10 );
251+ for (int j = 0 ; j < num_threads; ++j)
252+ {
253+ int start = j * points_per_thread;
254+ int end = (j == num_threads - 1 ) ? total_points : (j + 1 ) * points_per_thread;
255+ threads.emplace_back (count_points_mutex_2, start, end);
256+ }
257+ }
258+ auto end_time = std::chrono::high_resolution_clock::now ();
259+ measured_times.push_back (std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count ());
260+ }
261+ auto [min5, max5] = std::ranges::minmax_element (measured_times);
262+ average = (std::accumulate (measured_times.begin (), measured_times.end (), 0LL ) - (*min5 + *max5)) / (measured_times.size () - 2 );
263+ std::cout << " Average time (mutex) - start, end: " << average << " ms\n " ;
264+ std::cout << " Pi accuracy: " << 1 .f * in_mutex_2 / total_mutex_2 * 4 << " \n " ;
265+ std::cout << " In: " << in_mutex_2 << " Total: " << total_mutex_2 << " \n\n " ;
266+
267+ measured_times.clear ();
268+
269+ // --------------------------------------------------------------------------------
270+ // Future
271+ // --------------------------------------------------------------------------------
272+ std::vector<float > pi_values;
273+ int totalInCircle = 0 ;
274+ for (int i = 0 ; i < 10 ; ++i)
275+ {
276+ auto start_time = std::chrono::high_resolution_clock::now ();
277+ {
278+ std::vector<std::future<int >> futures;
279+ for (int j = 0 ; j < num_threads; ++j)
280+ {
281+ futures.push_back (std::async (std::launch::async, count_points_future_1, points_per_thread));
282+ }
283+ for (auto &future : futures)
284+ {
285+ totalInCircle += future.get ();
286+ }
287+ pi_values.push_back (1 .f * totalInCircle / total_points * 4 );
288+ if (i != 9 )
289+ totalInCircle = 0 ;
290+ }
291+ auto end_time = std::chrono::high_resolution_clock::now ();
292+ measured_times.push_back (std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count ());
293+ }
294+ auto [min6, max6] = std::ranges::minmax_element (measured_times);
295+ average = (std::accumulate (measured_times.begin (), measured_times.end (), 0LL ) - (*min6 + *max6)) / (measured_times.size () - 2 );
296+ std::cout << " Average time (future) - int: " << average << " ms\n " ;
297+ std::cout << " Pi accuracy: " << 1 .f * std::accumulate (pi_values.begin (), pi_values.end (), 0 .f ) / pi_values.size () << " \n " ;
298+ std::cout << " In: " << totalInCircle << " Total: " << total_points << " \n\n " ;
299+
300+ measured_times.clear ();
301+ pi_values.clear ();
302+ totalInCircle = 0 ;
303+
304+ for (int i = 0 ; i < 10 ; ++i)
305+ {
306+ auto start_time = std::chrono::high_resolution_clock::now ();
307+ {
308+ std::vector<std::future<int >> futures;
309+ for (int j = 0 ; j < num_threads; ++j)
310+ {
311+ int start = j * points_per_thread;
312+ int end = (j == num_threads - 1 ) ? total_points : (j + 1 ) * points_per_thread;
313+ futures.push_back (std::async (std::launch::async, count_points_future_2, start, end));
314+ }
315+ for (auto &future : futures)
316+ {
317+ totalInCircle += future.get ();
318+ }
319+ pi_values.push_back (1 .f * totalInCircle / total_points * 4 );
320+ if (i != 9 )
321+ totalInCircle = 0 ;
322+ }
323+ auto end_time = std::chrono::high_resolution_clock::now ();
324+ measured_times.push_back (std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count ());
325+ }
326+ auto [min7, max7] = std::ranges::minmax_element (measured_times);
327+ average = (std::accumulate (measured_times.begin (), measured_times.end (), 0LL ) - (*min7 + *max7)) / (measured_times.size () - 2 );
328+ std::cout << " Average time (future) - start, end: " << average << " ms\n " ;
329+ std::cout << " Pi accuracy: " << 1 .f * std::accumulate (pi_values.begin (), pi_values.end (), 0 .f ) / pi_values.size () << " \n " ;
330+ std::cout << " In: " << totalInCircle << " Total: " << total_points << " \n " ;
331+ }
0 commit comments