Galois
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
libgalois/include/galois/DynamicBitset.h
Go to the documentation of this file.
1 /*
2  * This file belongs to the Galois project, a C++ library for exploiting
3  * parallelism. The code is being released under the terms of the 3-Clause BSD
4  * License (a copy is located in LICENSE.txt at the top-level directory).
5  *
6  * Copyright (C) 2019, The University of Texas at Austin. All rights reserved.
7  * UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS
8  * SOFTWARE AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY,
9  * FITNESS FOR ANY PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF
10  * PERFORMANCE, AND ANY WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF
11  * DEALING OR USAGE OF TRADE. NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH
12  * RESPECT TO THE USE OF THE SOFTWARE OR DOCUMENTATION. Under no circumstances
13  * shall University be liable for incidental, special, indirect, direct or
14  * consequential damages or loss of profits, interruption of business, or
15  * related expenses which may arise from use of Software or Documentation,
16  * including but not limited to those resulting from defects in Software and/or
17  * Documentation, or loss or inaccuracy of data of any kind.
18  */
19 
26 #ifndef _GALOIS_DYNAMIC_BIT_SET_
27 #define _GALOIS_DYNAMIC_BIT_SET_
28 
29 #include <climits>
30 #include <vector>
31 #include <cassert>
32 
33 #include <boost/iterator/counting_iterator.hpp>
34 #include <boost/mpl/has_xxx.hpp>
35 
36 #include "galois/config.h"
37 #include "galois/AtomicWrapper.h"
40 #include "galois/Traits.h"
41 #include "galois/Galois.h"
42 
43 namespace galois {
48 protected:
50  size_t num_bits;
51  static constexpr uint32_t bits_uint64 = sizeof(uint64_t) * CHAR_BIT;
52 
53 public:
56 
63  const auto& get_vec() const { return bitvec; }
64 
71  auto& get_vec() { return bitvec; }
72 
78  void resize(uint64_t n) {
79  assert(bits_uint64 == 64); // compatibility with other devices
80  num_bits = n;
81  bitvec.resize((n + bits_uint64 - 1) / bits_uint64);
82  reset();
83  }
84 
90  void reserve(uint64_t n) {
91  assert(bits_uint64 == 64); // compatibility with other devices
92  bitvec.reserve((n + bits_uint64 - 1) / bits_uint64);
93  }
94 
99  size_t size() const { return num_bits; }
100 
105  // size_t alloc_size() const { return bitvec.size() * sizeof(uint64_t); }
106 
110  void reset() { std::fill(bitvec.begin(), bitvec.end(), 0); }
111 
118  void reset(size_t begin, size_t end) {
119  if (num_bits == 0)
120  return;
121 
122  assert(begin <= (num_bits - 1));
123  assert(end <= (num_bits - 1));
124 
125  // 100% safe implementation, but slow
126  // for (unsigned long i = begin; i <= end; i++) {
127  // size_t bit_index = i / bits_uint64;
128  // uint64_t bit_offset = 1;
129  // bit_offset <<= (i % bits_uint64);
130  // uint64_t mask = ~bit_offset;
131  // bitvec[bit_index] &= mask;
132  //}
133 
134  // block which you are safe to clear
135  size_t vec_begin = (begin + bits_uint64 - 1) / bits_uint64;
136  size_t vec_end;
137 
138  if (end == (num_bits - 1))
139  vec_end = bitvec.size();
140  else
141  vec_end = (end + 1) / bits_uint64; // floor
142 
143  if (vec_begin < vec_end) {
144  std::fill(bitvec.begin() + vec_begin, bitvec.begin() + vec_end, 0);
145  }
146 
147  vec_begin *= bits_uint64;
148  vec_end *= bits_uint64;
149 
150  // at this point vec_begin -> vec_end-1 has been reset
151 
152  if (vec_begin > vec_end) {
153  // no fill happened
154  if (begin < vec_begin) {
155  size_t diff = vec_begin - begin;
156  assert(diff < 64);
157  uint64_t mask = ((uint64_t)1 << (64 - diff)) - 1;
158 
159  size_t end_diff = end - vec_end + 1;
160  uint64_t or_mask = ((uint64_t)1 << end_diff) - 1;
161  mask |= ~or_mask;
162 
163  size_t bit_index = begin / bits_uint64;
164  bitvec[bit_index] &= mask;
165  }
166  } else {
167  if (begin < vec_begin) {
168  size_t diff = vec_begin - begin;
169  assert(diff < 64);
170  uint64_t mask = ((uint64_t)1 << (64 - diff)) - 1;
171  size_t bit_index = begin / bits_uint64;
172  bitvec[bit_index] &= mask;
173  }
174  if (end >= vec_end) {
175  size_t diff = end - vec_end + 1;
176  assert(diff < 64);
177  uint64_t mask = ((uint64_t)1 << diff) - 1;
178  size_t bit_index = end / bits_uint64;
179  bitvec[bit_index] &= ~mask;
180  }
181  }
182  }
183 
192  bool test(size_t index) const {
193  size_t bit_index = index / bits_uint64;
194  uint64_t bit_offset = 1;
195  bit_offset <<= (index % bits_uint64);
196  return ((bitvec[bit_index].load(std::memory_order_relaxed) & bit_offset) !=
197  0);
198  }
199 
206  bool set(size_t index) {
207  size_t bit_index = index / bits_uint64;
208  uint64_t bit_offset = 1;
209  bit_offset <<= (index % bits_uint64);
210  uint64_t old_val = bitvec[bit_index];
211  // test and set
212  // if old_bit is 0, then atomically set it
213  while (((old_val & bit_offset) == 0) &&
214  !bitvec[bit_index].compare_exchange_weak(
215  old_val, old_val | bit_offset, std::memory_order_relaxed))
216  ;
217  return (old_val & bit_offset);
218  }
219 
226  bool reset(size_t index) {
227  size_t bit_index = index / bits_uint64;
228  uint64_t bit_offset = 1;
229  bit_offset <<= (index % bits_uint64);
230  uint64_t old_val = bitvec[bit_index];
231  // test and reset
232  // if old_bit is 1, then atomically reset it
233  while (((old_val & bit_offset) != 0) &&
234  !bitvec[bit_index].compare_exchange_weak(
235  old_val, old_val & ~bit_offset, std::memory_order_relaxed))
236  ;
237  return (old_val & bit_offset);
238  }
239 
240  // assumes bit_vector is not updated (set) in parallel
241  void bitwise_or(const DynamicBitSet& other) {
242  assert(size() == other.size());
243  auto& other_bitvec = other.get_vec();
245  galois::iterate(size_t{0}, bitvec.size()),
246  [&](size_t i) { bitvec[i] |= other_bitvec[i]; }, galois::no_stats());
247  }
248 
249  // assumes bit_vector is not updated (set) in parallel
250 
256  void bitwise_and(const DynamicBitSet& other) {
257  assert(size() == other.size());
258  auto& other_bitvec = other.get_vec();
260  galois::iterate(size_t{0}, bitvec.size()),
261  [&](size_t i) { bitvec[i] &= other_bitvec[i]; }, galois::no_stats());
262  }
263 
271  void bitwise_and(const DynamicBitSet& other1, const DynamicBitSet& other2) {
272  assert(size() == other1.size());
273  assert(size() == other2.size());
274  auto& other_bitvec1 = other1.get_vec();
275  auto& other_bitvec2 = other2.get_vec();
276 
278  galois::iterate(size_t{0}, bitvec.size()),
279  [&](size_t i) { bitvec[i] = other_bitvec1[i] & other_bitvec2[i]; },
280  galois::no_stats());
281  }
282 
288  void bitwise_xor(const DynamicBitSet& other) {
289  assert(size() == other.size());
290  auto& other_bitvec = other.get_vec();
292  galois::iterate(size_t{0}, bitvec.size()),
293  [&](size_t i) { bitvec[i] ^= other_bitvec[i]; }, galois::no_stats());
294  }
295 
303  void bitwise_xor(const DynamicBitSet& other1, const DynamicBitSet& other2) {
304  assert(size() == other1.size());
305  assert(size() == other2.size());
306  auto& other_bitvec1 = other1.get_vec();
307  auto& other_bitvec2 = other2.get_vec();
308 
310  galois::iterate(size_t{0}, bitvec.size()),
311  [&](size_t i) { bitvec[i] = other_bitvec1[i] ^ other_bitvec2[i]; },
312  galois::no_stats());
313  }
314 
320  uint64_t count() const {
323  galois::iterate(bitvec.begin(), bitvec.end()),
324  [&](uint64_t n) {
325 #ifdef __GNUC__
326  ret += __builtin_popcountll(n);
327 #else
328  n = n - ((n >> 1) & 0x5555555555555555UL);
329  n = (n & 0x3333333333333333UL) + ((n >> 2) & 0x3333333333333333UL);
330  ret +=
331  (((n + (n >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >>
332  56;
333 #endif
334  },
335  galois::no_stats());
336  return ret.reduce();
337  }
338 
346  // TODO uint32_t is somewhat dangerous; change in the future
347  std::vector<uint32_t> getOffsets() const {
349  std::vector<unsigned int> tPrefixBitCounts(activeThreads);
350 
351  // count how many bits are set on each thread
352  galois::on_each([&](unsigned tid, unsigned nthreads) {
353  size_t start;
354  size_t end;
355  std::tie(start, end) =
356  galois::block_range((size_t)0, this->size(), tid, nthreads);
357 
358  unsigned int count = 0;
359  for (unsigned int i = start; i < end; ++i) {
360  if (this->test(i))
361  ++count;
362  }
363 
364  tPrefixBitCounts[tid] = count;
365  });
366 
367  // calculate prefix sum of bits per thread
368  for (unsigned int i = 1; i < activeThreads; ++i) {
369  tPrefixBitCounts[i] += tPrefixBitCounts[i - 1];
370  }
371 
372  // total num of set bits
373  uint64_t bitsetCount = tPrefixBitCounts[activeThreads - 1];
374  std::vector<uint32_t> offsets;
375 
376  // calculate the indices of the set bits and save them to the offset
377  // vector
378  if (bitsetCount > 0) {
379  offsets.resize(bitsetCount);
380  galois::on_each([&](unsigned tid, unsigned nthreads) {
381  size_t start;
382  size_t end;
383  std::tie(start, end) =
384  galois::block_range((size_t)0, this->size(), tid, nthreads);
385  unsigned int count = 0;
386  unsigned int tPrefixBitCount;
387  if (tid == 0) {
388  tPrefixBitCount = 0;
389  } else {
390  tPrefixBitCount = tPrefixBitCounts[tid - 1];
391  }
392 
393  for (unsigned int i = start; i < end; ++i) {
394  if (this->test(i)) {
395  offsets[tPrefixBitCount + count] = i;
396  ++count;
397  }
398  }
399  });
400  }
401 
402  return offsets;
403  }
404 
406  using tt_is_copyable = int;
407 };
408 
410 static galois::DynamicBitSet EmptyBitset;
411 
415  static constexpr bool is_vector_bitset() { return false; }
416 
418  static constexpr bool is_valid() { return false; }
419 
421  static galois::DynamicBitSet& get() { return EmptyBitset; }
422 
424  static void reset_range(size_t, size_t) {}
425 };
426 } // namespace galois
427 #endif
Definition: Traits.h:247
void bitwise_and(const DynamicBitSet &other)
Does an IN-PLACE bitwise and of this bitset and another bitset.
Definition: libgalois/include/galois/DynamicBitset.h:256
void reserve(uint64_t n)
Reserves capacity for the bitset.
Definition: libgalois/include/galois/DynamicBitset.h:90
auto & get_vec()
Returns the underlying bitset representation to the user.
Definition: libgalois/include/galois/DynamicBitset.h:71
unsigned int getActiveThreads() noexcept
Returns the number of threads in use.
Definition: Threads.cpp:37
DynamicBitSet()
Constructor which initializes to an empty bitset.
Definition: libgalois/include/galois/DynamicBitset.h:55
std::pair< IterTy, IterTy > block_range(IterTy b, IterTy e, unsigned id, unsigned num)
Returns a continuous block from the range based on the number of divisions and the id of the block re...
Definition: gstl.h:244
void bitwise_or(const DynamicBitSet &other)
Definition: libgalois/include/galois/DynamicBitset.h:241
std::vector< uint32_t > getOffsets() const
Returns a vector containing the set bits in this bitset in order from left to right.
Definition: libgalois/include/galois/DynamicBitset.h:347
Concurrent dynamically allocated bitset.
Definition: libgalois/include/galois/DynamicBitset.h:47
static constexpr bool is_vector_bitset()
Returns false as this is an empty bitset.
Definition: libgalois/include/galois/DynamicBitset.h:415
static constexpr bool is_valid()
Returns false as this is an empty bitset (invalid)
Definition: libgalois/include/galois/DynamicBitset.h:418
bool test(size_t index) const
Check a bit to see if it is currently set.
Definition: libgalois/include/galois/DynamicBitset.h:192
void reset(size_t begin, size_t end)
Unset a range of bits given an inclusive range.
Definition: libgalois/include/galois/DynamicBitset.h:118
uint64_t count() const
Count how many bits are set in the bitset.
Definition: libgalois/include/galois/DynamicBitset.h:320
int tt_is_copyable
this is defined to
Definition: libgalois/include/galois/DynamicBitset.h:406
void resize(uint64_t n)
Resizes the bitset.
Definition: libgalois/include/galois/DynamicBitset.h:78
void reset()
Gets the space taken by the bitset.
Definition: libgalois/include/galois/DynamicBitset.h:110
static constexpr uint32_t bits_uint64
Definition: libgalois/include/galois/DynamicBitset.h:51
unsigned int activeThreads
Definition: Threads.cpp:26
size_t size() const
Gets the size of the bitset.
Definition: libgalois/include/galois/DynamicBitset.h:99
A structure representing an empty bitset.
Definition: libgalois/include/galois/DynamicBitset.h:413
bool reset(size_t index)
Reset a bit in the bitset.
Definition: libgalois/include/galois/DynamicBitset.h:226
void do_all(const RangeFunc &rangeMaker, FunctionTy &&fn, const Args &...args)
Standard do-all loop.
Definition: Loops.h:71
bool set(size_t index)
Set a bit in the bitset.
Definition: libgalois/include/galois/DynamicBitset.h:206
void on_each(FunctionTy &&fn, const Args &...args)
Low-level parallel loop.
Definition: Loops.h:86
T & reduce()
Returns the final reduction value.
Definition: Reduction.h:102
Contains a copyable atomics class.
void bitwise_and(const DynamicBitSet &other1, const DynamicBitSet &other2)
Does an IN-PLACE bitwise and of 2 passed in bitsets and saves to this bitset.
Definition: libgalois/include/galois/DynamicBitset.h:271
static void reset_range(size_t, size_t)
No-op since it&#39;s an empty bitset.
Definition: libgalois/include/galois/DynamicBitset.h:424
galois::PODResizeableArray< galois::CopyableAtomic< uint64_t > > bitvec
Definition: libgalois/include/galois/DynamicBitset.h:49
size_t num_bits
Definition: libgalois/include/galois/DynamicBitset.h:50
auto iterate(C &cont)
Definition: Range.h:323
This is a container that encapsulates a resizeable array of plain-old-datatype (POD) elements...
Definition: PODResizeableArray.h:40
const auto & get_vec() const
Returns the underlying bitset representation to the user.
Definition: libgalois/include/galois/DynamicBitset.h:63
void bitwise_xor(const DynamicBitSet &other1, const DynamicBitSet &other2)
Does an IN-PLACE bitwise and of 2 passed in bitsets and saves to this bitset.
Definition: libgalois/include/galois/DynamicBitset.h:303
void bitwise_xor(const DynamicBitSet &other)
Does an IN-PLACE bitwise xor of this bitset and another bitset.
Definition: libgalois/include/galois/DynamicBitset.h:288