C++ Library Extensions 2022.12.09
To help learn modern C++ programming
tpf_split_ranges.hpp
Go to the documentation of this file.
1/*
2 Programmed by: Thomas Kim
3 First Version: Nov. 12, 2017
4 Last Version: Jan. 28, 2020
5*/
6
7#ifndef _TPF_SPLIT_RANGES_HPP
8#define _TPF_SPLIT_RANGES_HPP
9
10#ifndef NOMINMAX
11#define NOMINMAX
12#endif
13
14#ifdef _MSVC_LANG
15 #if _MSVC_LANG < 201703L
16 #error This libary requires C++17 Standard (Visual Studio 2017).
17 #endif
18#else
19
20 #if __cplusplus < 201703
21 #error This library requires C++17 Standard (GNU g++ version 8.0 or clang++ version 8.0 above)
22 #endif // end of __cplusplus
23
24#endif // end of _MSVC_LANG
25
26#include <tpf_types.hpp>
27#include <iostream>
28#include <iomanip>
29#include <locale>
30#include <sstream>
31#include <thread>
32#include <functional>
33#include <future>
34#include <utility>
35
36namespace tpf
37{
38 namespace split_range
39 {
40 template<typename T>
41 using range = std::pair<T, T>;
42
44
45 template<typename T>
46 using rngvctr = std::deque<range<T>>;
47
49
50 template<typename T> std::string
51 display_range(const range<T>& rg, bool thousands=true)
52 {
53 std::ostringstream os;
54
55 if (thousands)
56 os.imbue(std::locale(""));
57 os << "[" << rg.first << ", " << rg.second << ") - count: " << (rg.second - rg.first);
58
59 return os.str();
60 }
61
62 template<typename T> rngvctr<T>
63 split_range_count(T st, T ed, T count=(T)(-1));
64
65 template<typename T> rngvctr<T>
66 split_range_span(T st, T ed, T min_span=(T)(-1));
67
68 template<typename T> std::string
69 display_ranges(const rngvctr<T>& rngs, int width=9, bool thousands=true);
70
71 inline size_t cpu_thread_count() noexcept;
72
73 /*
74 st ed
75 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
76
77 minimum span = 3; [1, 5), [5, 8), [8, 11)
78
79 1, 2, 3, 4, 5 -> [1, 5) : 4
80 5, 6, 7, 8 -> [5, 8) : 3
81 8, 9, 10, 11 -> [8, 11): 3
82 */
83 template<typename T> rngvctr<T>
84 split_range_span(T st, T ed, T min_span)
85 {
86 T dist = ed - st;
87
88 if(min_span == (T)(-1))
89 {
90 min_span = dist / (T) cpu_thread_count();
91 if(min_span == 0) min_span = dist;
92 }
93
94 T rnd = dist % min_span;
95 T count = dist / min_span;
96
97 rngvctr<T> rng;
98 if (rnd == 0)
99 {
100 T prev = st;
101 for (T i{}; i < count; ++i)
102 {
103 //rng.emplace_back(range<T>(prev, prev + min_span));
104 rng.emplace_back(prev, prev + min_span);
105 prev += min_span;
106 }
107
108 return rng;
109 }
110 else
111 {
112 if (count >= rnd)
113 {
114 T span1 = min_span + 1;
115
116 T prev = st;
117 for (T i{}; i < rnd; ++i)
118 {
119 // rng.emplace_back(range<T>(prev, prev + span1));
120 rng.emplace_back(prev, prev + span1);
121 prev += span1;
122 }
123
124 count = count - rnd;
125 for (T i{}; i < count; ++i)
126 {
127 // rng.emplace_back(range<T>(prev, prev + min_span));
128 rng.emplace_back(prev, prev + min_span);
129 prev += min_span;
130 }
131
132 return rng;
133 }
134 else
135 {
136 return split_range_count(st, ed, count);
137 }
138 }
139 }
140
141 template<typename T>
142 rngvctr<T> split_range_count(T st, T ed, T count /* = cpu_thread_count */)
143 {
144 if(count == (T)(-1))
145 count = (T)cpu_thread_count();
146
147 T dist = ed - st;
148 T span = dist / count;
149 T rnd = dist % count;
150
151 rngvctr<T> rng;
152
153 if (rnd == 0)
154 {
155 T prev = st;
156 for (T i{}; i < count; ++i)
157 {
158 //rng.push_back(range<T>(prev, prev + span));
159 rng.emplace_back(prev, prev + span);
160 prev += span;
161 }
162
163 return rng;
164 }
165 else
166 {
167 T span1 = span + 1;
168
169 T prev = st;
170 for (T i{}; i < rnd; ++i)
171 {
172 // rng.emplace_back(range<T>(prev, prev + span1));
173 rng.emplace_back(prev, prev + span1);
174 prev += span1;
175 }
176 // rnd = dist % count;
177 // rnd cannot be greater than count
178 count = count - rnd;
179 for (T i{}; i < count; ++i)
180 {
181 // rng.emplace_back(range<T>(prev, prev + span));
182 rng.emplace_back(prev, prev + span);
183 prev += span;
184 }
185
186 return rng;
187 }
188 }
189
190 template<typename T>
192 {
193 return split_range_count(st, ed, T{2});
194 }
195
196 template<typename T> std::string
197 display_ranges(const rngvctr<T>& rngs, int width, bool thousands)
198 {
199 if (!rngs.empty())
200 {
201 std::ostringstream os;
202
203 if (thousands)
204 os.imbue(std::locale(""));
205
206 os << "Range count: " << rngs.size() << ", "<<
207 "Max range: " << (rngs[0].second - rngs[0].first) <<
208 " elements, Min range: " << (rngs.back().second - rngs.back().first)
209 << " elements."<<std::endl<<std::endl;
210
211 size_t count = (size_t)rngs.size();
212
213 for(size_t i = 0; i<count; ++i)
214 {
215 os<<std::setw(3)<<i<<": [" << std::setw(width)
216 << rngs[(size_t)i].first << ", " << std::setw(width)
217 << rngs[(size_t)i].second << ") "<< std::setw(width)
218 <<(rngs[(size_t)i].second - rngs[(size_t)i].first)
219 << " elements" << std::endl;
220 }
221
222 return os.str();
223 }
224 else
225 return std::string("Range empty.");
226 }
227
228 inline size_t cpu_thread_count() noexcept
229 {
230 return (size_t) std::thread::hardware_concurrency();
231 }
232
233 template<typename RangeType, typename BinOperation, typename SumupOperation,
234 typename = std::enable_if_t<tpf::types::is_integer_v<RangeType>>>
235 auto parallel_reduce(RangeType start, RangeType end,
236 BinOperation&& bin_opr, SumupOperation&& sumup,
237 size_t use_thread_count = tpf::InvalidIndex)
238 {
239 using return_t = decltype(bin_opr(start, end));
240
241 if(use_thread_count == tpf::InvalidIndex)
242 use_thread_count = cpu_thread_count();
243
244 auto ranges =
245 split_range_count((size_t)start, (size_t)end, use_thread_count);
246
247 using future_t = std::future<return_t>;
248 using futures_t = std::vector<future_t>;
249
250 futures_t futures;
251
252 if constexpr (!std::is_void_v<return_t>)
253 {
254 if(ranges.size()==1)
255 return bin_opr(start, end);
256 else
257 {
258 size_t count = ranges.size();
259
260 for(size_t i{1}; i < count; ++i)
261 futures.emplace_back(std::async(std::launch::async,
262 bin_opr, ranges[i].first, ranges[i].second));
263
264 auto sum =
265 bin_opr(ranges.front().first, ranges.front().second);
266
267 for(auto& f: futures)
268 sum = sumup(sum, f.get());
269
270 return sum;
271 }
272 }
273 else
274 {
275 if(ranges.size()==1)
276 bin_opr(start, end);
277 else
278 {
279 size_t count = ranges.size();
280
281 for(size_t i{1}; i < count; ++i)
282 futures.emplace_back(std::async(std::launch::async,
283 bin_opr, ranges[i].first, ranges[i].second));
284
285 bin_opr(ranges.front().first, ranges.front().second);
286
287 for(auto& f: futures)
288 sumup(sum, f.get());
289 }
290
291 }
292 }
293
294 template<typename ContainerType, typename BinOperation, typename SumupOperation,
296 auto parallel_reduce(ContainerType&& container,
297 BinOperation&& bin_opr, SumupOperation&& sumup,
298 size_t use_thread_count = tpf::InvalidIndex)
299 {
300 using return_t = decltype(bin_opr(size_t{}, size_t{}));
301 size_t end = container.size();
302
303 if constexpr(!std::is_void_v<return_t>)
304 {
305 return parallel_reduce(size_t{}, end,
306 std::forward<BinOperation>(bin_opr), std::forward<SumupOperation>(sumup),
307 use_thread_count);
308 }
309 else
310 {
311 parallel_reduce(size_t{}, end,
312 std::forward<BinOperation>(bin_opr), std::forward<SumupOperation>(sumup),
313 use_thread_count);
314 }
315 }
316 } // end of namespace range
317
318} // end of namespace tpf
319
320#endif // end of _TPF_SPLIT_RANGES_HPP
return_t sum(int a, int b)
int return_t
std::atomic< int > count
Definition: 022-mutex.cpp:10
auto & endl
constexpr size_t InvalidIndex
Definition: cpg_types.hpp:219
range< size_t > range_t
auto parallel_reduce(RangeType start, RangeType end, BinOperation &&bin_opr, SumupOperation &&sumup, size_t use_thread_count=tpf::InvalidIndex)
rngvctr< T > split_range_span(T st, T ed, T min_span=(T)(-1))
rngvctr< size_t > range_vctr_t
rngvctr< T > split_range_half(T st, T ed)
std::string display_range(const range< T > &rg, bool thousands=true)
std::pair< T, T > range
std::string display_ranges(const rngvctr< T > &rngs, int width=9, bool thousands=true)
size_t cpu_thread_count() noexcept
rngvctr< T > split_range_count(T st, T ed, T count=(T)(-1))
std::deque< range< T > > rngvctr
hidden::enable_if_container_type_t< Type, ReturnType > enable_if_container_type_t
Definition: tpf_types.hpp:5564
Includes subnamespace conversion.
Definition: 31-visit.cpp:7
Type functions are implemented.