]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/include/parallel/multiseq_selection.h
types.h: Move enum parallelism here.
[thirdparty/gcc.git] / libstdc++-v3 / include / parallel / multiseq_selection.h
1 // -*- C++ -*-
2
3 // Copyright (C) 2007, 2008 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the terms
7 // of the GNU General Public License as published by the Free Software
8 // Foundation; either version 2, or (at your option) any later
9 // version.
10
11 // This library is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15
16 // You should have received a copy of the GNU General Public License
17 // along with this library; see the file COPYING. If not, write to
18 // the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
19 // MA 02111-1307, USA.
20
21 // As a special exception, you may use this file as part of a free
22 // software library without restriction. Specifically, if other files
23 // instantiate templates or use macros or inline functions from this
24 // file, or you compile this file and link it with other files to
25 // produce an executable, this file does not by itself cause the
26 // resulting executable to be covered by the GNU General Public
27 // License. This exception does not however invalidate any other
28 // reasons why the executable file might be covered by the GNU General
29 // Public License.
30
31 /** @file parallel/multiseq_selection.h
32 * @brief Functions to find elements of a certain global rank in
33 * multiple sorted sequences. Also serves for splitting such
34 * sequence sets.
35 *
36 * The algorithm description can be found in
37 *
38 * P. J. Varman, S. D. Scheufler, B. R. Iyer, and G. R. Ricard.
39 * Merging Multiple Lists on Hierarchical-Memory Multiprocessors.
40 * Journal of Parallel and Distributed Computing, 12(2):171–177, 1991.
41 *
42 * This file is a GNU parallel extension to the Standard C++ Library.
43 */
44
45 // Written by Johannes Singler.
46
47 #ifndef _GLIBCXX_PARALLEL_MULTISEQ_SELECTION_H
48 #define _GLIBCXX_PARALLEL_MULTISEQ_SELECTION_H 1
49
50 #include <vector>
51 #include <queue>
52
53 #include <bits/stl_algo.h>
54
55 #include <parallel/sort.h>
56
57 namespace __gnu_parallel
58 {
59 /** @brief Compare a pair of types lexicographically, ascending. */
60 template<typename T1, typename T2, typename Comparator>
61 class lexicographic
62 : public std::binary_function<std::pair<T1, T2>, std::pair<T1, T2>, bool>
63 {
64 private:
65 Comparator& comp;
66
67 public:
68 lexicographic(Comparator& _comp) : comp(_comp) { }
69
70 bool
71 operator()(const std::pair<T1, T2>& p1,
72 const std::pair<T1, T2>& p2) const
73 {
74 if (comp(p1.first, p2.first))
75 return true;
76
77 if (comp(p2.first, p1.first))
78 return false;
79
80 // Firsts are equal.
81 return p1.second < p2.second;
82 }
83 };
84
85 /** @brief Compare a pair of types lexicographically, descending. */
86 template<typename T1, typename T2, typename Comparator>
87 class lexicographic_reverse : public std::binary_function<T1, T2, bool>
88 {
89 private:
90 Comparator& comp;
91
92 public:
93 lexicographic_reverse(Comparator& _comp) : comp(_comp) { }
94
95 bool
96 operator()(const std::pair<T1, T2>& p1,
97 const std::pair<T1, T2>& p2) const
98 {
99 if (comp(p2.first, p1.first))
100 return true;
101
102 if (comp(p1.first, p2.first))
103 return false;
104
105 // Firsts are equal.
106 return p2.second < p1.second;
107 }
108 };
109
110 /**
111 * @brief Splits several sorted sequences at a certain global rank,
112 * resulting in a splitting point for each sequence.
113 * The sequences are passed via a sequence of random-access
114 * iterator pairs, none of the sequences may be empty. If there
115 * are several equal elements across the split, the ones on the
116 * left side will be chosen from sequences with smaller number.
117 * @param begin_seqs Begin of the sequence of iterator pairs.
118 * @param end_seqs End of the sequence of iterator pairs.
119 * @param rank The global rank to partition at.
120 * @param begin_offsets A random-access sequence begin where the
121 * result will be stored in. Each element of the sequence is an
122 * iterator that points to the first element on the greater part of
123 * the respective sequence.
124 * @param comp The ordering functor, defaults to std::less<T>.
125 */
126 template<typename RanSeqs, typename RankType, typename RankIterator,
127 typename Comparator>
128 void
129 multiseq_partition(RanSeqs begin_seqs, RanSeqs end_seqs,
130 RankType rank,
131 RankIterator begin_offsets,
132 Comparator comp = std::less<
133 typename std::iterator_traits<typename
134 std::iterator_traits<RanSeqs>::value_type::
135 first_type>::value_type>()) // std::less<T>
136 {
137 _GLIBCXX_CALL(end_seqs - begin_seqs)
138
139 typedef typename std::iterator_traits<RanSeqs>::value_type::first_type
140 It;
141 typedef typename std::iterator_traits<It>::difference_type
142 difference_type;
143 typedef typename std::iterator_traits<It>::value_type value_type;
144
145 lexicographic<value_type, int, Comparator> lcomp(comp);
146 lexicographic_reverse<value_type, int, Comparator> lrcomp(comp);
147
148 // Number of sequences, number of elements in total (possibly
149 // including padding).
150 difference_type m = std::distance(begin_seqs, end_seqs), N = 0,
151 nmax, n, r;
152
153 for (int i = 0; i < m; i++)
154 N += std::distance(begin_seqs[i].first, begin_seqs[i].second);
155
156 if (rank == N)
157 {
158 for (int i = 0; i < m; i++)
159 begin_offsets[i] = begin_seqs[i].second; // Very end.
160 // Return m - 1;
161 }
162
163 _GLIBCXX_PARALLEL_ASSERT(m != 0 && N != 0 && rank >= 0 && rank < N);
164
165 difference_type* ns = new difference_type[m];
166 difference_type* a = new difference_type[m];
167 difference_type* b = new difference_type[m];
168 difference_type l;
169
170 ns[0] = std::distance(begin_seqs[0].first, begin_seqs[0].second);
171 nmax = ns[0];
172 for (int i = 0; i < m; i++)
173 {
174 ns[i] = std::distance(begin_seqs[i].first, begin_seqs[i].second);
175 nmax = std::max(nmax, ns[i]);
176 }
177
178 r = log2(nmax) + 1;
179
180 // Pad all lists to this length, at least as long as any ns[i],
181 // equality iff nmax = 2^k - 1.
182 l = (1ULL << r) - 1;
183
184 // From now on, including padding.
185 N = l * m;
186
187 for (int i = 0; i < m; i++)
188 {
189 a[i] = 0;
190 b[i] = l;
191 }
192 n = l / 2;
193
194 // Invariants:
195 // 0 <= a[i] <= ns[i], 0 <= b[i] <= l
196
197 #define S(i) (begin_seqs[i].first)
198
199 // Initial partition.
200 std::vector<std::pair<value_type, int> > sample;
201
202 for (int i = 0; i < m; i++)
203 if (n < ns[i]) //sequence long enough
204 sample.push_back(std::make_pair(S(i)[n], i));
205 __gnu_sequential::sort(sample.begin(), sample.end(), lcomp);
206
207 for (int i = 0; i < m; i++) //conceptual infinity
208 if (n >= ns[i]) //sequence too short, conceptual infinity
209 sample.push_back(std::make_pair(S(i)[0] /*dummy element*/, i));
210
211 difference_type localrank = rank * m / N ;
212
213 int j;
214 for (j = 0; j < localrank && ((n + 1) <= ns[sample[j].second]); ++j)
215 a[sample[j].second] += n + 1;
216 for (; j < m; j++)
217 b[sample[j].second] -= n + 1;
218
219 // Further refinement.
220 while (n > 0)
221 {
222 n /= 2;
223
224 int lmax_seq = -1; // to avoid warning
225 const value_type* lmax = NULL; // impossible to avoid the warning?
226 for (int i = 0; i < m; i++)
227 {
228 if (a[i] > 0)
229 {
230 if (!lmax)
231 {
232 lmax = &(S(i)[a[i] - 1]);
233 lmax_seq = i;
234 }
235 else
236 {
237 // Max, favor rear sequences.
238 if (!comp(S(i)[a[i] - 1], *lmax))
239 {
240 lmax = &(S(i)[a[i] - 1]);
241 lmax_seq = i;
242 }
243 }
244 }
245 }
246
247 int i;
248 for (i = 0; i < m; i++)
249 {
250 difference_type middle = (b[i] + a[i]) / 2;
251 if (lmax && middle < ns[i] &&
252 lcomp(std::make_pair(S(i)[middle], i),
253 std::make_pair(*lmax, lmax_seq)))
254 a[i] = std::min(a[i] + n + 1, ns[i]);
255 else
256 b[i] -= n + 1;
257 }
258
259 difference_type leftsize = 0, total = 0;
260 for (int i = 0; i < m; i++)
261 {
262 leftsize += a[i] / (n + 1);
263 total += l / (n + 1);
264 }
265
266 difference_type skew = static_cast<difference_type>
267 (static_cast<uint64_t>(total) * rank / N - leftsize);
268
269 if (skew > 0)
270 {
271 // Move to the left, find smallest.
272 std::priority_queue<std::pair<value_type, int>,
273 std::vector<std::pair<value_type, int> >,
274 lexicographic_reverse<value_type, int, Comparator> >
275 pq(lrcomp);
276
277 for (int i = 0; i < m; i++)
278 if (b[i] < ns[i])
279 pq.push(std::make_pair(S(i)[b[i]], i));
280
281 for (; skew != 0 && !pq.empty(); --skew)
282 {
283 int source = pq.top().second;
284 pq.pop();
285
286 a[source] = std::min(a[source] + n + 1, ns[source]);
287 b[source] += n + 1;
288
289 if (b[source] < ns[source])
290 pq.push(std::make_pair(S(source)[b[source]], source));
291 }
292 }
293 else if (skew < 0)
294 {
295 // Move to the right, find greatest.
296 std::priority_queue<std::pair<value_type, int>,
297 std::vector<std::pair<value_type, int> >,
298 lexicographic<value_type, int, Comparator> > pq(lcomp);
299
300 for (int i = 0; i < m; i++)
301 if (a[i] > 0)
302 pq.push(std::make_pair(S(i)[a[i] - 1], i));
303
304 for (; skew != 0; ++skew)
305 {
306 int source = pq.top().second;
307 pq.pop();
308
309 a[source] -= n + 1;
310 b[source] -= n + 1;
311
312 if (a[source] > 0)
313 pq.push(std::make_pair(S(source)[a[source] - 1], source));
314 }
315 }
316 }
317
318 // Postconditions:
319 // a[i] == b[i] in most cases, except when a[i] has been clamped
320 // because of having reached the boundary
321
322 // Now return the result, calculate the offset.
323
324 // Compare the keys on both edges of the border.
325
326 // Maximum of left edge, minimum of right edge.
327 value_type* maxleft = NULL;
328 value_type* minright = NULL;
329 for (int i = 0; i < m; i++)
330 {
331 if (a[i] > 0)
332 {
333 if (!maxleft)
334 maxleft = &(S(i)[a[i] - 1]);
335 else
336 {
337 // Max, favor rear sequences.
338 if (!comp(S(i)[a[i] - 1], *maxleft))
339 maxleft = &(S(i)[a[i] - 1]);
340 }
341 }
342 if (b[i] < ns[i])
343 {
344 if (!minright)
345 minright = &(S(i)[b[i]]);
346 else
347 {
348 // Min, favor fore sequences.
349 if (comp(S(i)[b[i]], *minright))
350 minright = &(S(i)[b[i]]);
351 }
352 }
353 }
354
355 int seq = 0;
356 for (int i = 0; i < m; i++)
357 begin_offsets[i] = S(i) + a[i];
358
359 delete[] ns;
360 delete[] a;
361 delete[] b;
362 }
363
364
365 /**
366 * @brief Selects the element at a certain global rank from several
367 * sorted sequences.
368 *
369 * The sequences are passed via a sequence of random-access
370 * iterator pairs, none of the sequences may be empty.
371 * @param begin_seqs Begin of the sequence of iterator pairs.
372 * @param end_seqs End of the sequence of iterator pairs.
373 * @param rank The global rank to partition at.
374 * @param offset The rank of the selected element in the global
375 * subsequence of elements equal to the selected element. If the
376 * selected element is unique, this number is 0.
377 * @param comp The ordering functor, defaults to std::less.
378 */
379 template<typename T, typename RanSeqs, typename RankType,
380 typename Comparator>
381 T
382 multiseq_selection(RanSeqs begin_seqs, RanSeqs end_seqs, RankType rank,
383 RankType& offset, Comparator comp = std::less<T>())
384 {
385 _GLIBCXX_CALL(end_seqs - begin_seqs)
386
387 typedef typename std::iterator_traits<RanSeqs>::value_type::first_type
388 It;
389 typedef typename std::iterator_traits<It>::difference_type
390 difference_type;
391
392 lexicographic<T, int, Comparator> lcomp(comp);
393 lexicographic_reverse<T, int, Comparator> lrcomp(comp);
394
395 // Number of sequences, number of elements in total (possibly
396 // including padding).
397 difference_type m = std::distance(begin_seqs, end_seqs);
398 difference_type N = 0;
399 difference_type nmax, n, r;
400
401 for (int i = 0; i < m; i++)
402 N += std::distance(begin_seqs[i].first, begin_seqs[i].second);
403
404 if (m == 0 || N == 0 || rank < 0 || rank >= N)
405 {
406 // Result undefined when there is no data or rank is outside bounds.
407 throw std::exception();
408 }
409
410
411 difference_type* ns = new difference_type[m];
412 difference_type* a = new difference_type[m];
413 difference_type* b = new difference_type[m];
414 difference_type l;
415
416 ns[0] = std::distance(begin_seqs[0].first, begin_seqs[0].second);
417 nmax = ns[0];
418 for (int i = 0; i < m; ++i)
419 {
420 ns[i] = std::distance(begin_seqs[i].first, begin_seqs[i].second);
421 nmax = std::max(nmax, ns[i]);
422 }
423
424 r = log2(nmax) + 1;
425
426 // Pad all lists to this length, at least as long as any ns[i],
427 // equality iff nmax = 2^k - 1
428 l = pow2(r) - 1;
429
430 // From now on, including padding.
431 N = l * m;
432
433 for (int i = 0; i < m; ++i)
434 {
435 a[i] = 0;
436 b[i] = l;
437 }
438 n = l / 2;
439
440 // Invariants:
441 // 0 <= a[i] <= ns[i], 0 <= b[i] <= l
442
443 #define S(i) (begin_seqs[i].first)
444
445 // Initial partition.
446 std::vector<std::pair<T, int> > sample;
447
448 for (int i = 0; i < m; i++)
449 if (n < ns[i])
450 sample.push_back(std::make_pair(S(i)[n], i));
451 __gnu_sequential::sort(sample.begin(), sample.end(),
452 lcomp, sequential_tag());
453
454 // Conceptual infinity.
455 for (int i = 0; i < m; i++)
456 if (n >= ns[i])
457 sample.push_back(std::make_pair(S(i)[0] /*dummy element*/, i));
458
459 difference_type localrank = rank * m / N ;
460
461 int j;
462 for (j = 0; j < localrank && ((n + 1) <= ns[sample[j].second]); ++j)
463 a[sample[j].second] += n + 1;
464 for (; j < m; ++j)
465 b[sample[j].second] -= n + 1;
466
467 // Further refinement.
468 while (n > 0)
469 {
470 n /= 2;
471
472 const T* lmax = NULL;
473 for (int i = 0; i < m; ++i)
474 {
475 if (a[i] > 0)
476 {
477 if (!lmax)
478 lmax = &(S(i)[a[i] - 1]);
479 else
480 {
481 if (comp(*lmax, S(i)[a[i] - 1])) //max
482 lmax = &(S(i)[a[i] - 1]);
483 }
484 }
485 }
486
487 int i;
488 for (i = 0; i < m; i++)
489 {
490 difference_type middle = (b[i] + a[i]) / 2;
491 if (lmax && middle < ns[i] && comp(S(i)[middle], *lmax))
492 a[i] = std::min(a[i] + n + 1, ns[i]);
493 else
494 b[i] -= n + 1;
495 }
496
497 difference_type leftsize = 0, total = 0;
498 for (int i = 0; i < m; ++i)
499 {
500 leftsize += a[i] / (n + 1);
501 total += l / (n + 1);
502 }
503
504 difference_type skew = ((unsigned long long)total * rank / N
505 - leftsize);
506
507 if (skew > 0)
508 {
509 // Move to the left, find smallest.
510 std::priority_queue<std::pair<T, int>,
511 std::vector<std::pair<T, int> >,
512 lexicographic_reverse<T, int, Comparator> > pq(lrcomp);
513
514 for (int i = 0; i < m; ++i)
515 if (b[i] < ns[i])
516 pq.push(std::make_pair(S(i)[b[i]], i));
517
518 for (; skew != 0 && !pq.empty(); --skew)
519 {
520 int source = pq.top().second;
521 pq.pop();
522
523 a[source] = std::min(a[source] + n + 1, ns[source]);
524 b[source] += n + 1;
525
526 if (b[source] < ns[source])
527 pq.push(std::make_pair(S(source)[b[source]], source));
528 }
529 }
530 else if (skew < 0)
531 {
532 // Move to the right, find greatest.
533 std::priority_queue<std::pair<T, int>,
534 std::vector<std::pair<T, int> >,
535 lexicographic<T, int, Comparator> > pq(lcomp);
536
537 for (int i = 0; i < m; ++i)
538 if (a[i] > 0)
539 pq.push(std::make_pair(S(i)[a[i] - 1], i));
540
541 for (; skew != 0; ++skew)
542 {
543 int source = pq.top().second;
544 pq.pop();
545
546 a[source] -= n + 1;
547 b[source] -= n + 1;
548
549 if (a[source] > 0)
550 pq.push(std::make_pair(S(source)[a[source] - 1], source));
551 }
552 }
553 }
554
555 // Postconditions:
556 // a[i] == b[i] in most cases, except when a[i] has been clamped
557 // because of having reached the boundary
558
559 // Now return the result, calculate the offset.
560
561 // Compare the keys on both edges of the border.
562
563 // Maximum of left edge, minimum of right edge.
564 bool maxleftset = false, minrightset = false;
565
566 // Impossible to avoid the warning?
567 T maxleft, minright;
568 for (int i = 0; i < m; ++i)
569 {
570 if (a[i] > 0)
571 {
572 if (!maxleftset)
573 {
574 maxleft = S(i)[a[i] - 1];
575 maxleftset = true;
576 }
577 else
578 {
579 // Max.
580 if (comp(maxleft, S(i)[a[i] - 1]))
581 maxleft = S(i)[a[i] - 1];
582 }
583 }
584 if (b[i] < ns[i])
585 {
586 if (!minrightset)
587 {
588 minright = S(i)[b[i]];
589 minrightset = true;
590 }
591 else
592 {
593 // Min.
594 if (comp(S(i)[b[i]], minright))
595 minright = S(i)[b[i]];
596 }
597 }
598 }
599
600 // Minright is the splitter, in any case.
601
602 if (!maxleftset || comp(minright, maxleft))
603 {
604 // Good luck, everything is split unambiguously.
605 offset = 0;
606 }
607 else
608 {
609 // We have to calculate an offset.
610 offset = 0;
611
612 for (int i = 0; i < m; ++i)
613 {
614 difference_type lb = std::lower_bound(S(i), S(i) + ns[i],
615 minright,
616 comp) - S(i);
617 offset += a[i] - lb;
618 }
619 }
620
621 delete[] ns;
622 delete[] a;
623 delete[] b;
624
625 return minright;
626 }
627 }
628
629 #undef S
630
631 #endif
632