HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_ConcurrentClassifier.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_ConcurrentClassifier.h (UT Library, C++)
7  *
8  * Lock-free parallel disjoint set data structure (aka UNION-FIND)
9  * with path compression and union by rank
10  *
11  * Supports concurrent find(), same() and unite() calls as described
12  * in the paper (renamed here to classRoot(), areInSameClass(), and
13  * unionClasses(), respectively.
14  *
15  * "Wait-free Parallel Algorithms for the Union-Find Problem"
16  * by Richard J. Anderson and Heather Woll
17  *
18  * In addition, this class supports optimistic locking (try_lock/unlock)
19  * of disjoint sets and a combined unite+unlock operation.
20  *
21  * Based on the freely available implementation by Wenzel Jakob on GitHub.
22  */
23 
24 
25 #ifndef __UT_ConcurrentClassifier__
26 #define __UT_ConcurrentClassifier__
27 
28 #include <SYS/SYS_Types.h>
29 #include <SYS/SYS_TypeDecorate.h>
30 #include "UT_Array.h"
31 
32 #if defined(ARM64)
33 #include <sse2neon.h>
34 #else
35 #include <immintrin.h>
36 #endif
37 
38 SYS_DECLARE_LEGACY_TR(std::atomic<uint64>)
39 
41 {
42 public:
44  { if (size > 0) makeClass(size); }
45 
46  uint32 makeClass(uint32 num_classes = 1)
47  {
48  uint32 s = myData.size();
49  myData.setSizeNoInit(s + num_classes);
50  for (int i = 0, ie = num_classes; i < ie; ++i)
51  myData(s + i) = (uint32) i;
52  return s;
53  }
54 
56  {
57  while (id != parent(id)) {
58  uint64 value = myData(id);
59  uint32 new_parent = parent((uint32) value);
60  uint64 new_value =
61  (value & 0xFFFFFFFF00000000ULL) | new_parent;
62 
63  /* Try to update parent (may fail, that's ok) */
64  if (value != new_value)
65  myData(id).compare_exchange_weak(value, new_value);
66  id = new_parent;
67  }
68  return id;
69  }
70 
71  bool areInSameClass(uint32 id1, uint32 id2) const
72  {
73  for ( ; ; )
74  {
75  id1 = classRoot(id1);
76  id2 = classRoot(id2);
77  if (id1 == id2)
78  return true;
79  if (parent(id1) == id1)
80  return false;
81  }
82  }
83 
85  {
86  for ( ; ; )
87  {
88  id1 = classRoot(id1);
89  id2 = classRoot(id2);
90 
91  if (id1 == id2)
92  return id1;
93 
94  uint32 r1 = rank(id1), r2 = rank(id2);
95 
96  if (r1 > r2 || (r1 == r2 && id1 < id2))
97  {
98  UTswap(r1, r2);
99  UTswap(id1, id2);
100  }
101 
102  uint64 old_entry = ((uint64_t) r1 << 32) | id1;
103  uint64 new_entry = ((uint64_t) r1 << 32) | id2;
104 
105  if (!myData(id1).compare_exchange_strong(old_entry, new_entry))
106  continue;
107 
108  if (r1 == r2) {
109  old_entry = ((uint64_t) r2 << 32) | id2;
110  new_entry = ((uint64_t) (r2+1) << 32) | id2;
111 
112  // Try to update the rank (may fail, that's ok)
113  myData(id2).compare_exchange_weak(old_entry, new_entry);
114  }
115 
116  break;
117  }
118  return id2;
119  }
120 
121  // Try to lock a disjoint union identified by one of its elements (this can
122  // occasionally fail when there are concurrent operations). The parameter
123  // 'id' will be updated to store the current representative ID of the union.
124  bool try_lock(uint32 &id)
125  {
126  const uint64 lock_flag = 1ULL << 63;
127  id = classRoot(id);
128  uint64 value = myData(id);
129  if ((value & lock_flag) || (uint32) value != id)
130  return false;
131 
132  // On IA32/x64, a PAUSE instruction is recommended for CAS busy loops.
133 #if 0
134  #if defined(__i386__) || defined(__amd64__)
135  __asm__ __volatile__ ("pause\n");
136  #endif
137 #endif
138  _mm_pause();
139  return myData(id).compare_exchange_strong(value, value | lock_flag);
140  }
141 
142  void unlock(uint32 id)
143  {
144  const uint64 lock_flag = 1ULL << 63;
145  myData(id) &= ~lock_flag;
146  }
147 
148 
149  // Return the representative index of the set that results from merging
150  // locked disjoint sets 'id1' and 'id2'.
152  {
153  uint32 r1 = rank(id1), r2 = rank(id2);
154  return (r1 > r2 || (r1 == r2 && id1 < id2)) ? id1 : id2;
155  }
156 
157 
158  // Atomically unite two locked disjoint sets and unlock them. Assumes
159  // that here are no other concurrent unite() involving the same sets.
161  {
162  uint32 r1 = rank(id1), r2 = rank(id2);
163 
164  if (r1 > r2 || (r1 == r2 && id1 < id2))
165  {
166  UTswap(r1, r2);
167  UTswap(id1, id2);
168  }
169 
170  myData(id1) = ((uint64) r1 << 32) | id2;
171  myData(id2) = ((uint64) (r2 + ((r1 == r2) ? 1 : 0)) << 32) | id2;
172 
173  return id2;
174  }
175 
176  uint32 size() const { return (uint32) myData.size(); }
177  uint32 entries() const { return size(); }
178 
180  {
181  return tbb::parallel_reduce(tbb::blocked_range<uint32>(0, size()),
182  uint32(0),
183  [&](tbb::blocked_range<uint32> r, uint32 total)
184  {
185  for (uint32 i = r.begin(); i < r.end();
186  ++i)
187  total += (i == classRoot(i));
188 
189  return total;
190  }, std::plus<uint32>());
191  }
192 
193 private:
195  uint32 rank(uint32 id) const
196  { return ((uint32) (myData[id] >> 32)) & 0x7FFFFFFFu; }
197 
199  uint32 parent(uint32_t id) const
200  { return (uint32_t) myData(id); }
201 
202  using DataEntry = std::atomic<uint64>;
203  using DataArray = UT_Array<DataEntry>;
204  mutable DataArray myData;
205 };
206 
207 #endif
bool areInSameClass(uint32 id1, uint32 id2) const
int int32
Definition: SYS_Types.h:39
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the and then *wait for them to all complete We provide a helper class
Definition: thread.h:623
void UTswap(T &a, T &b)
Definition: UT_Swap.h:35
GLdouble s
Definition: glad.h:3009
uint32 unionClasses(uint32 id1, uint32 id2)
unsigned long long uint64
Definition: SYS_Types.h:117
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
GLuint id
Definition: glcorearb.h:655
SYS_DECLARE_LEGACY_TR(GU_Detail)
uint32 unite_unlock(uint32 id1, uint32 id2)
GLsizeiptr size
Definition: glcorearb.h:664
uint32 classRoot(uint32 id) const
size_t *lastDimSize unsigned int rank
Definition: wrapArray.h:334
unsigned int uint32
Definition: SYS_Types.h:40
Definition: core.h:1131
uint32 unite_index_locked(uint32 id1, uint32 id2) const
uint32 makeClass(uint32 num_classes=1)
GLboolean r
Definition: glcorearb.h:1222