C++ Library Extensions 2022.12.09
To help learn modern C++ programming
016-parallel_permutation.cpp
Go to the documentation of this file.
1#include <tpf_set.hpp>
3#include <tpf_ncrnpr.hpp>
4#include <tpf_output.hpp>
5
6namespace types = tpf::types;
7namespace set = tpf::set;
8namespace crs = tpf::chrono_random;
9namespace cpr = tpf::ncrnpr;
10
12
13namespace tuv // tutorial video
14{
16 {
17 // this will be used for read-only
18 // so, we do not safeguard this variable
20
21 // this variable will be shared among threads
22 // so, we have to safeguard from HALF-READ
23 std::atomic<int> current_thread_count;
24
25 // to synchronize stream console output
27 };
28
29 template<template<typename, typename...> class ContainerType,
30 typename EleType, typename... Types, typename... OuterTypes>
31 void enum_permu(ContainerType<ContainerType<EleType, Types...>, OuterTypes...>& permutations,
32 ContainerType<EleType, Types...>& L,
33 ContainerType<EleType, Types...>& R)
34 {
35 if(L.empty())
36 {
37 permutations.emplace_back(R);
38 }
39 else
40 {
41 size_t size = L.size();
42
43 // make a room for last element
44 R.emplace_back(EleType{});
45
46 /*
47 if an exception is thrown before R.pop_back() is reached
48 this program does not work properly
49
50 we need to handle this issue in EXCEPTION-SAFE manner
51 */
52
53 auto r_pop_back = [&R](auto ptr) { R.pop_back(); };
54
55 using r_type_t = std::remove_reference_t<decltype(R)>;
56
57 // clean_up is also a local variable
58 std::unique_ptr<r_type_t, decltype(r_pop_back)>
59 clean_up(&R, r_pop_back);
60
61 for(size_t i_th = 0; i_th < size; ++i_th)
62 {
63 // copy L to copied_L
64 auto copied_L = L;
65
66 // remove i_th element from copied_L
67 copied_L.erase(copied_L.begin()+i_th);
68
69 // move i_th element from L to R
70 // R.emplace_back( L[i_th] );
71 R.back() = L[i_th];
72
73 enum_permu(permutations,copied_L, R);
74 }
75 }
76 }
77
78 template<typename NRType>
79 auto build_permutations(NRType n)
80 {
81 using element_t = short;
82 using container_t = std::vector;
83
87
88 set_t L; L.reserve(n);
89 for(size_t i = 0; i < (size_t)n; ++i)
90 L.emplace_back((element_t)i);
91
92 set_t R; R.reserve(n);
93
94 sets_t permutations;
95
96 enum_permu(permutations, L, R);
97
98 return permutations;
99 }
100
102 template<typename CallbackType, template<typename, typename...> class ContainerType,
103 typename EleType, typename... Types>
104 void enum_permu(CallbackType&& callback, // callback function
105 ContainerType<EleType, Types...>& L, ContainerType<EleType, Types...>& R)
106 {
107 if(L.empty())
108 {
109 callback(R);
110 }
111 else
112 {
113 size_t size = L.size();
114
115 // make a room for last element
116 R.emplace_back(EleType{});
117
118 /*
119 if an exception is thrown before R.pop_back() is reached
120 this program does not work properly
121
122 we need to handle this issue in EXCEPTION-SAFE manner
123 */
124
125 auto r_pop_back = [&R](auto ptr) { R.pop_back(); };
126
127 using r_type_t = std::remove_reference_t<decltype(R)>;
128
129 // clean_up is also a local variable
130 std::unique_ptr<r_type_t, decltype(r_pop_back)>
131 clean_up(&R, r_pop_back);
132
133 for(size_t i_th = 0; i_th < size; ++i_th)
134 {
135 // copy L to copied_L
136 auto copied_L = L;
137
138 // remove i_th element from copied_L
139 copied_L.erase(copied_L.begin()+i_th);
140
141 // move i_th element from L to R
142 // R.emplace_back( L[i_th] );
143 R.back() = L[i_th];
144
145 enum_permu(std::forward<CallbackType>(callback), copied_L, R);
146 }
147 }
148 }
149
150 template<typename CallbackType, typename NRType>
151 void build_permutations(CallbackType&& callback, NRType n)
152 {
153 using element_t = short;
154 using container_t = std::vector;
155
159
160 set_t L; L.reserve(n);
161 for(size_t i = 0; i < (size_t)n; ++i)
162 L.emplace_back((element_t)i);
163
164 set_t R; R.reserve(n);
165
166 enum_permu(std::forward<CallbackType>(callback), L, R);
167 }
168
170 template<typename CallbackType, template<typename, typename...> class ContainerType,
171 typename EleType, typename... Types>
172 void enum_permu(thread_bundle& tb, CallbackType&& callback, // callback function
173 ContainerType<EleType, Types...>& L, ContainerType<EleType, Types...>& R)
174 {
175 using set_t = ContainerType<EleType, Types...>;
176
177 if(L.empty())
178 {
179 callback(R);
180 }
181 else
182 {
183 size_t size = L.size();
184
185 // make a room for last element
186 R.emplace_back(EleType{});
187
188 /*
189 if an exception is thrown before R.pop_back() is reached
190 this program does not work properly
191
192 we need to handle this issue in EXCEPTION-SAFE manner
193 */
194
195 auto r_pop_back = [&R](auto ptr) { R.pop_back(); };
196
197 using r_type_t = std::remove_reference_t<decltype(R)>;
198
199 // clean_up is also a local variable
200 std::unique_ptr<r_type_t, decltype(r_pop_back)>
201 clean_up(&R, r_pop_back);
202
203 using future_t = std::future<void>; // void, beause enum_permu() does not return
204 using futures_t = std::vector<future_t>;
205
206 futures_t futures; // std::vector<std::future<void>>
207
208 for(size_t i_th = 0; i_th < size; ++i_th)
209 {
210 // copy L to copied_L
211 auto copied_L = L;
212
213 // remove i_th element from copied_L
214 copied_L.erase(copied_L.begin()+i_th);
215
216 // move i_th element from L to R
217 // R.emplace_back( L[i_th] );
218 R.back() = L[i_th];
219
220 if(size < 7 || tb.current_thread_count >= tb.max_thread_count)
221 enum_permu(tb, std::forward<CallbackType>(callback), copied_L, R);
222 else
223 {
224 auto go_parallel = [&tb, &callback, L = set_t{copied_L}, R = set_t{R}]() mutable
225 {
226 enum_permu(tb, callback, L, R);
227 };
228
229 futures.emplace_back(std::async(std::launch::async, go_parallel));
231 }
232 }
233
234 for(auto& f: futures)
235 {
236 f.wait(); --tb.current_thread_count;
237 }
238 }
239 }
240
241 template<typename CallbackType, typename NRType>
242 void build_permutations(thread_bundle& tb, CallbackType&& callback, NRType n)
243 {
244 using element_t = short;
245 using container_t = std::vector;
246
250
251 set_t L; L.reserve(n);
252 for(size_t i = 0; i < (size_t)n; ++i)
253 L.emplace_back((element_t)i);
254
255 set_t R; R.reserve(n);
256
257 enum_permu(tb, std::forward<CallbackType>(callback), L, R);
258 }
259
260} // end of namespace tuv or tutorial video
261
263{
264 auto permu = cpr::npr(n, n);
265 stream <<"Generating "<< permu << " permutations!" << tpf::endl;
266
268 auto permutations = tuv::build_permutations(n);
269 stream << "Elapsed: " << sw.elapsed_time<crs::second_t>() << tpf::endl;
270
271 for(auto& p: permutations)
272 stream << p << tpf::nl;
273
274 // if we don't flush, we cannot see the result
276}
277
278template<typename ThreadFactorType>
279void performance_test_build_permutations(ThreadFactorType factor, int n)
280{
281 auto permu = cpr::npr(n, n);
282 stream <<"Generating "<< permu << " permutations!" << tpf::endl;
283
285
286 tuv::thread_bundle tb{ (int) std::thread::hardware_concurrency() * factor };
287
288 auto callback_parallel = [&tb, &output](auto p)
289 {
290 if(p.size() < 6)
291 {
292 std::unique_lock<std::mutex> lock(tb.mutex);
293 output << p << " - " << std::this_thread::get_id() << tpf::endl;
294 }
295
296 // more real code based of permutation p
297 };
298
299 auto callback_sequential = [&output](auto p)
300 {
301 if(p.size() < 6)
302 {
303 output << p << " - " << std::this_thread::get_id() << tpf::endl;
304 }
305 // more real code based of permutation p
306 };
307
309
310 stream <<"Generating permutations in parallel." << tpf::endl;
311 tuv::build_permutations(tb, callback_parallel, n);
312 stream << "Elapsed: " << sw.elapsed_time<crs::second_t>() << tpf::endl;
313
314 stream <<"Generating permutations in sequence." << tpf::endl;
315 tuv::build_permutations(callback_sequential, n);
316 stream << "Elapsed: " << sw.elapsed_time<crs::second_t>() << tpf::endl;
317}
318
319int main(int argc, char* argv[])
320{
321 if(argc > 2) // two or more commandline arguments
322 {
323 auto factor = atoi(argv[1]);
324 auto n = atoi(argv[2]);
325
326 stream << "Using " << factor* std::thread::hardware_concurrency()
327 << " threads..."<< tpf::endl;
328
329 // test_build_permutations(n);
331 }
332 else
333 {
334 stream <<"Usage: " << argv[0] <<" factor n" << tpf::endl;
335 stream <<"\tfactor is for hardware thread multiplier" << tpf::endl;
336 stream <<"\tn is for factorial size" << tpf::endl;
337 }
338}
339
340
int main(int argc, char *argv[])
void performance_test_build_permutations(ThreadFactorType factor, int n)
tpf::sstream stream
void test_build_permutations(int n)
std::mutex mutex
Definition: 022-mutex.cpp:12
void go_parallel(Policy &&policy, IteratorBegin &&begin, IteratorEnd &&end, Callback &&callback)
std::deque< element_t > set_t
Definition: 061-subsets.cpp:9
std::deque< set_t > sets_t
Definition: 061-subsets.cpp:10
std::string elapsed_time(bool bReset=true, TimeUnit dummy_time=TimeUnit{}) const
Implements random number generator and stop watch.
std::ratio< 1 > second_t
Implements nCr, nPr.
Definition: tpf_ncrnpr.hpp:64
enable_if_all_in_list_t< types::type_list_t< Type1, Type2 >, integral_list_t, ReturnType > npr(Type1 nn, Type2 rr)
Definition: tpf_ncrnpr.hpp:428
Stream output operators are defined.
Definition: tpf_output.hpp:58
Implements set operations.
Definition: tpf_set.hpp:52
Type to string name conversions are defined.
Definition: 31-visit.cpp:7
constexpr auto flush
Definition: tpf_output.hpp:970
typename SetTagType::set_t set_t
Definition: tpf_types.hpp:1966
constexpr auto endl
Definition: tpf_output.hpp:973
constexpr auto nl
Definition: tpf_output.hpp:971
typename SetTagType::sets_t sets_t
Definition: tpf_types.hpp:1969
auto build_permutations(NRType n)
void enum_permu(ContainerType< ContainerType< EleType, Types... >, OuterTypes... > &permutations, ContainerType< EleType, Types... > &L, ContainerType< EleType, Types... > &R)
std::atomic< int > current_thread_count
constexpr int factor
Stream output operators << are implemented.
Implements set operations.