OmniSciDB  95562058bd
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Intervals.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 OmniSci, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * @file Intervals.h
19  * @author Matt Pulver <matt.pulver@omnisci.com>
20  * @description Divide up indexes (A, A+1, A+2, ..., B-2, B-1) among
21  * N workers as evenly as possible in a range-based for loop:
22  * for (auto const& interval : makeIntervals(A, B, N)) {}
23  * where interval is a 2-member struct of (begin,end) values.
24  * This just does the arithmetic to divide up work into chunks;
25  * asynchronous thread management is done separately.
26  *
27  * This is a common pattern when dividing work among CPU threads.
28  * This is NOT appropriate for GPU threads which are better done with strides.
29  * 2 guarantees:
30  * - Larger intervals always precede smaller intervals.
31  * - The difference in work between any two intervals never exceeds 1.
32  *
33  * Example Usage. Divide up this loop:
34  *
35  * for (int i=0 ; i<100 ; ++i)
36  * work(i);
37  *
38  * into 3 parallel workers:
39  *
40  * for (auto const& interval : makeIntervals(0, 100, 3))
41  * spawn_thread(work, interval);
42  *
43  * where, for example, spawn_thread spawns threads running:
44  *
45  * for (int j=interval.begin ; j<interval.end ; ++j)
46  * work(j);
47  *
48  * This would be equivalent to running the following 3 loops in parallel:
49  *
50  * for (int j= 0 ; j< 34 ; ++j) work(j);
51  * for (int j=34 ; j< 67 ; ++j) work(j);
52  * for (int j=67 ; j<100 ; ++j) work(j);
53  *
54  * The number of iterations in this example is 34, 33, 33, respectively.
55  * In general the larger iterations always precede the smaller, and the
56  * maximum difference between the largest and smallest never exceeds 1.
57  */
58 
59 #pragma once
60 
61 #include <limits>
62 #include <type_traits>
63 
64 template <typename T>
65 struct Interval {
66  T const begin;
67  T const end;
68 };
69 
70 template <typename T>
71 class Intervals {
73  T const begin_;
75  Unsigned const quot_;
76  Unsigned const rem_;
77 
78  Intervals(T begin, T end, Unsigned n_workers)
79  : begin_(begin)
80  , total_size_(begin < end && n_workers ? end - begin : 0)
81  , quot_(n_workers ? total_size_ / n_workers : 0)
82  , rem_(n_workers ? total_size_ % n_workers : 0) {
83  static_assert(std::is_integral_v<T>);
84  }
85 
86  public:
87  class Iterator {
89  Unsigned const quot_;
91 
92  public:
94  : begin_(begin), quot_(quot), rem_(rem) {}
95  // bool in arithmetic context is 0 or 1.
96  Interval<T> operator*() const { return {begin_, T(begin_ + quot_ + bool(rem_))}; }
97  void operator++() { begin_ += quot_ + (rem_ && rem_--); }
98  bool operator!=(Iterator const& rhs) const { return begin_ != rhs.begin_; }
99  };
100 
101  Iterator begin() { return {begin_, quot_, rem_}; }
102  Iterator end() { return {static_cast<T>(begin_ + total_size_), quot_, 0}; }
103  template <typename U>
104  friend Intervals<U> makeIntervals(U begin, U end, std::size_t n_workers);
105 };
106 
107 template <typename T>
108 Intervals<T> makeIntervals(T begin, T end, std::size_t n_workers) {
109  using Unsigned = typename std::make_unsigned<T>::type;
110  if constexpr (sizeof(Unsigned) < sizeof(std::size_t)) {
111  if (std::numeric_limits<Unsigned>::max() < n_workers) {
112  n_workers = std::numeric_limits<Unsigned>::max();
113  }
114  }
115  return {begin, end, static_cast<Unsigned>(n_workers)};
116 }
Unsigned const quot_
Definition: Intervals.h:75
Unsigned const total_size_
Definition: Intervals.h:74
Iterator begin()
Definition: Intervals.h:101
Intervals(T begin, T end, Unsigned n_workers)
Definition: Intervals.h:78
T const end
Definition: Intervals.h:67
Unsigned const rem_
Definition: Intervals.h:76
Unsigned const quot_
Definition: Intervals.h:89
bool operator!=(Iterator const &rhs) const
Definition: Intervals.h:98
Iterator end()
Definition: Intervals.h:102
T const begin_
Definition: Intervals.h:73
Iterator(T begin, Unsigned quot, Unsigned rem)
Definition: Intervals.h:93
T const begin
Definition: Intervals.h:66
Interval< T > operator*() const
Definition: Intervals.h:96
typename std::make_unsigned< T >::type Unsigned
Definition: Intervals.h:72
friend Intervals< U > makeIntervals(U begin, U end, std::size_t n_workers)