Kokkos Core Kernels Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Kokkos_Bitset.hpp
1 //@HEADER
2 // ************************************************************************
3 //
4 // Kokkos v. 4.0
5 // Copyright (2022) National Technology & Engineering
6 // Solutions of Sandia, LLC (NTESS).
7 //
8 // Under the terms of Contract DE-NA0003525 with NTESS,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
12 // See https://kokkos.org/LICENSE for license information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //@HEADER
16 
17 #ifndef KOKKOS_BITSET_HPP
18 #define KOKKOS_BITSET_HPP
19 #ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
20 #define KOKKOS_IMPL_PUBLIC_INCLUDE
21 #define KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_BITSET
22 #endif
23 
24 #include <Kokkos_Core.hpp>
25 #include <Kokkos_Functional.hpp>
26 
27 #include <impl/Kokkos_Bitset_impl.hpp>
28 
29 namespace Kokkos {
30 
31 template <typename Device = Kokkos::DefaultExecutionSpace>
32 class Bitset;
33 
34 template <typename Device = Kokkos::DefaultExecutionSpace>
36 
37 template <typename DstDevice, typename SrcDevice>
38 void deep_copy(Bitset<DstDevice>& dst, Bitset<SrcDevice> const& src);
39 
40 template <typename DstDevice, typename SrcDevice>
41 void deep_copy(Bitset<DstDevice>& dst, ConstBitset<SrcDevice> const& src);
42 
43 template <typename DstDevice, typename SrcDevice>
44 void deep_copy(ConstBitset<DstDevice>& dst, ConstBitset<SrcDevice> const& src);
45 
47 template <typename Device>
48 class Bitset {
49  public:
50  using execution_space = typename Device::execution_space;
51  using size_type = unsigned int;
52 
53  static constexpr unsigned BIT_SCAN_REVERSE = 1u;
54  static constexpr unsigned MOVE_HINT_BACKWARD = 2u;
55 
56  static constexpr unsigned BIT_SCAN_FORWARD_MOVE_HINT_FORWARD = 0u;
57  static constexpr unsigned BIT_SCAN_REVERSE_MOVE_HINT_FORWARD =
58  BIT_SCAN_REVERSE;
59  static constexpr unsigned BIT_SCAN_FORWARD_MOVE_HINT_BACKWARD =
60  MOVE_HINT_BACKWARD;
61  static constexpr unsigned BIT_SCAN_REVERSE_MOVE_HINT_BACKWARD =
62  BIT_SCAN_REVERSE | MOVE_HINT_BACKWARD;
63 
64  private:
65  enum : unsigned {
66  block_size = static_cast<unsigned>(sizeof(unsigned) * CHAR_BIT)
67  };
68  enum : unsigned { block_mask = block_size - 1u };
69  enum : unsigned {
70  block_shift = Kokkos::Impl::integral_power_of_two(block_size)
71  };
72 
74  using block_view_type = View<unsigned*, Device, MemoryTraits<RandomAccess>>;
75 
76  public:
77  Bitset() = default;
78 
80  Bitset(unsigned arg_size) : Bitset(Kokkos::view_alloc(), arg_size) {}
81 
82  template <class... P>
83  Bitset(const Impl::ViewCtorProp<P...>& arg_prop, unsigned arg_size)
84  : m_size(arg_size), m_last_block_mask(0u) {
86  using alloc_prop_t = std::decay_t<decltype(arg_prop)>;
87  static_assert(alloc_prop_t::initialize,
88  "Allocation property 'initialize' should be true.");
89  static_assert(
90  !alloc_prop_t::has_pointer,
91  "Allocation properties should not contain the 'pointer' property.");
92 
94  const auto prop_copy =
95  Impl::with_properties_if_unset(arg_prop, std::string("Bitset"));
96  m_blocks =
97  block_view_type(prop_copy, ((m_size + block_mask) >> block_shift));
98 
99  for (int i = 0, end = static_cast<int>(m_size & block_mask); i < end; ++i) {
100  m_last_block_mask |= 1u << i;
101  }
102  }
103 
104  KOKKOS_DEFAULTED_FUNCTION
105  Bitset(const Bitset<Device>&) = default;
106 
107  KOKKOS_DEFAULTED_FUNCTION
108  Bitset& operator=(const Bitset<Device>&) = default;
109 
110  KOKKOS_DEFAULTED_FUNCTION
111  Bitset(Bitset<Device>&&) = default;
112 
113  KOKKOS_DEFAULTED_FUNCTION
114  Bitset& operator=(Bitset<Device>&&) = default;
115 
116  KOKKOS_DEFAULTED_FUNCTION
117  ~Bitset() = default;
118 
121  KOKKOS_FORCEINLINE_FUNCTION
122  unsigned size() const { return m_size; }
123 
126  unsigned count() const {
127  Impl::BitsetCount<Bitset<Device>> f(*this);
128  return f.apply();
129  }
130 
133  void set() {
134  Kokkos::deep_copy(m_blocks, ~0u);
135 
136  if (m_last_block_mask) {
137  // clear the unused bits in the last block
138  Kokkos::Impl::DeepCopy<typename Device::memory_space, Kokkos::HostSpace>(
139  m_blocks.data() + (m_blocks.extent(0) - 1u), &m_last_block_mask,
140  sizeof(unsigned));
141  Kokkos::fence(
142  "Bitset::set: fence after clearing unused bits copying from "
143  "HostSpace");
144  }
145  }
146 
149  void reset() { Kokkos::deep_copy(m_blocks, 0u); }
150 
153  void clear() { Kokkos::deep_copy(m_blocks, 0u); }
154 
157  KOKKOS_FORCEINLINE_FUNCTION
158  bool set(unsigned i) const {
159  if (i < m_size) {
160  unsigned* block_ptr = &m_blocks[i >> block_shift];
161  const unsigned mask = 1u << static_cast<int>(i & block_mask);
162 
163  return !(atomic_fetch_or(block_ptr, mask) & mask);
164  }
165  return false;
166  }
167 
170  KOKKOS_FORCEINLINE_FUNCTION
171  bool reset(unsigned i) const {
172  if (i < m_size) {
173  unsigned* block_ptr = &m_blocks[i >> block_shift];
174  const unsigned mask = 1u << static_cast<int>(i & block_mask);
175 
176  return atomic_fetch_and(block_ptr, ~mask) & mask;
177  }
178  return false;
179  }
180 
183  KOKKOS_FORCEINLINE_FUNCTION
184  bool test(unsigned i) const {
185  if (i < m_size) {
186 #ifdef KOKKOS_ENABLE_SYCL
187  const unsigned block = Kokkos::atomic_load(&m_blocks[i >> block_shift]);
188 #else
189  const unsigned block = volatile_load(&m_blocks[i >> block_shift]);
190 #endif
191  const unsigned mask = 1u << static_cast<int>(i & block_mask);
192  return block & mask;
193  }
194  return false;
195  }
196 
200  KOKKOS_FORCEINLINE_FUNCTION
201  unsigned max_hint() const { return m_blocks.extent(0); }
202 
207  KOKKOS_INLINE_FUNCTION
209  unsigned hint,
210  unsigned scan_direction = BIT_SCAN_FORWARD_MOVE_HINT_FORWARD) const {
211  const unsigned block_idx =
212  (hint >> block_shift) < m_blocks.extent(0) ? (hint >> block_shift) : 0;
213  const unsigned offset = hint & block_mask;
214 #ifdef KOKKOS_ENABLE_SYCL
215  unsigned block = Kokkos::atomic_load(&m_blocks[block_idx]);
216 #else
217  unsigned block = volatile_load(&m_blocks[block_idx]);
218 #endif
219  block = !m_last_block_mask || (block_idx < (m_blocks.extent(0) - 1))
220  ? block
221  : block & m_last_block_mask;
222 
223  return find_any_helper(block_idx, offset, block, scan_direction);
224  }
225 
230  KOKKOS_INLINE_FUNCTION
232  unsigned hint,
233  unsigned scan_direction = BIT_SCAN_FORWARD_MOVE_HINT_FORWARD) const {
234  const unsigned block_idx = hint >> block_shift;
235  const unsigned offset = hint & block_mask;
236 #ifdef KOKKOS_ENABLE_SYCL
237  unsigned block = Kokkos::atomic_load(&m_blocks[block_idx]);
238 #else
239  unsigned block = volatile_load(&m_blocks[block_idx]);
240 #endif
241  block = !m_last_block_mask || (block_idx < (m_blocks.extent(0) - 1))
242  ? ~block
243  : ~block & m_last_block_mask;
244 
245  return find_any_helper(block_idx, offset, block, scan_direction);
246  }
247 
248  KOKKOS_INLINE_FUNCTION constexpr bool is_allocated() const {
249  return m_blocks.is_allocated();
250  }
251 
252  private:
253  KOKKOS_FORCEINLINE_FUNCTION
254  Kokkos::pair<bool, unsigned> find_any_helper(unsigned block_idx,
255  unsigned offset, unsigned block,
256  unsigned scan_direction) const {
257  Kokkos::pair<bool, unsigned> result(block > 0u, 0);
258 
259  if (!result.first) {
260  result.second = update_hint(block_idx, offset, scan_direction);
261  } else {
262  result.second =
263  scan_block((block_idx << block_shift), offset, block, scan_direction);
264  }
265  return result;
266  }
267 
268  KOKKOS_FORCEINLINE_FUNCTION
269  unsigned scan_block(unsigned block_start, int offset, unsigned block,
270  unsigned scan_direction) const {
271  offset = !(scan_direction & BIT_SCAN_REVERSE)
272  ? offset
273  : (offset + block_mask) & block_mask;
274  block = Impl::rotate_right(block, offset);
275  return (((!(scan_direction & BIT_SCAN_REVERSE)
276  ? Impl::bit_scan_forward(block)
277  : Impl::int_log2(block)) +
278  offset) &
279  block_mask) +
280  block_start;
281  }
282 
283  KOKKOS_FORCEINLINE_FUNCTION
284  unsigned update_hint(long long block_idx, unsigned offset,
285  unsigned scan_direction) const {
286  block_idx += scan_direction & MOVE_HINT_BACKWARD ? -1 : 1;
287  block_idx = block_idx >= 0 ? block_idx : m_blocks.extent(0) - 1;
288  block_idx =
289  block_idx < static_cast<long long>(m_blocks.extent(0)) ? block_idx : 0;
290 
291  return static_cast<unsigned>(block_idx) * block_size + offset;
292  }
293 
294  private:
295  unsigned m_size = 0;
296  unsigned m_last_block_mask = 0;
297  block_view_type m_blocks;
298 
299  private:
300  template <typename DDevice>
301  friend class Bitset;
302 
303  template <typename DDevice>
304  friend class ConstBitset;
305 
306  template <typename Bitset>
307  friend struct Impl::BitsetCount;
308 
309  template <typename DstDevice, typename SrcDevice>
310  friend void deep_copy(Bitset<DstDevice>& dst, Bitset<SrcDevice> const& src);
311 
312  template <typename DstDevice, typename SrcDevice>
313  friend void deep_copy(Bitset<DstDevice>& dst,
314  ConstBitset<SrcDevice> const& src);
315 };
316 
319 template <typename Device>
320 class ConstBitset {
321  public:
322  using execution_space = typename Device::execution_space;
323  using size_type = unsigned int;
324  using block_view_type = typename Bitset<Device>::block_view_type::const_type;
325 
326  private:
327  enum { block_size = static_cast<unsigned>(sizeof(unsigned) * CHAR_BIT) };
328  enum { block_mask = block_size - 1u };
329  enum { block_shift = Kokkos::Impl::integral_power_of_two(block_size) };
330 
331  public:
332  KOKKOS_FUNCTION
333  ConstBitset() : m_size(0) {}
334 
335  KOKKOS_FUNCTION
336  ConstBitset(Bitset<Device> const& rhs)
337  : m_size(rhs.m_size), m_blocks(rhs.m_blocks) {}
338 
339  KOKKOS_FUNCTION
340  ConstBitset(ConstBitset<Device> const& rhs)
341  : m_size(rhs.m_size), m_blocks(rhs.m_blocks) {}
342 
343  KOKKOS_FUNCTION
344  ConstBitset<Device>& operator=(Bitset<Device> const& rhs) {
345  this->m_size = rhs.m_size;
346  this->m_blocks = rhs.m_blocks;
347 
348  return *this;
349  }
350 
351  KOKKOS_FUNCTION
352  ConstBitset<Device>& operator=(ConstBitset<Device> const& rhs) {
353  this->m_size = rhs.m_size;
354  this->m_blocks = rhs.m_blocks;
355 
356  return *this;
357  }
358 
359  KOKKOS_FORCEINLINE_FUNCTION
360  unsigned size() const { return m_size; }
361 
362  unsigned count() const {
363  Impl::BitsetCount<ConstBitset<Device>> f(*this);
364  return f.apply();
365  }
366 
367  KOKKOS_FORCEINLINE_FUNCTION
368  bool test(unsigned i) const {
369  if (i < m_size) {
370  const unsigned block = m_blocks[i >> block_shift];
371  const unsigned mask = 1u << static_cast<int>(i & block_mask);
372  return block & mask;
373  }
374  return false;
375  }
376 
377  private:
378  unsigned m_size;
379  block_view_type m_blocks;
380 
381  private:
382  template <typename DDevice>
383  friend class ConstBitset;
384 
385  template <typename Bitset>
386  friend struct Impl::BitsetCount;
387 
388  template <typename DstDevice, typename SrcDevice>
389  friend void deep_copy(Bitset<DstDevice>& dst,
390  ConstBitset<SrcDevice> const& src);
391 
392  template <typename DstDevice, typename SrcDevice>
393  friend void deep_copy(ConstBitset<DstDevice>& dst,
394  ConstBitset<SrcDevice> const& src);
395 };
396 
397 template <typename DstDevice, typename SrcDevice>
398 void deep_copy(Bitset<DstDevice>& dst, Bitset<SrcDevice> const& src) {
399  if (dst.size() != src.size()) {
400  Kokkos::Impl::throw_runtime_exception(
401  "Error: Cannot deep_copy bitsets of different sizes!");
402  }
403 
404  Kokkos::fence("Bitset::deep_copy: fence before copy operation");
405  Kokkos::Impl::DeepCopy<typename DstDevice::memory_space,
406  typename SrcDevice::memory_space>(
407  dst.m_blocks.data(), src.m_blocks.data(),
408  sizeof(unsigned) * src.m_blocks.extent(0));
409  Kokkos::fence("Bitset::deep_copy: fence after copy operation");
410 }
411 
412 template <typename DstDevice, typename SrcDevice>
413 void deep_copy(Bitset<DstDevice>& dst, ConstBitset<SrcDevice> const& src) {
414  if (dst.size() != src.size()) {
415  Kokkos::Impl::throw_runtime_exception(
416  "Error: Cannot deep_copy bitsets of different sizes!");
417  }
418 
419  Kokkos::fence("Bitset::deep_copy: fence before copy operation");
420  Kokkos::Impl::DeepCopy<typename DstDevice::memory_space,
421  typename SrcDevice::memory_space>(
422  dst.m_blocks.data(), src.m_blocks.data(),
423  sizeof(unsigned) * src.m_blocks.extent(0));
424  Kokkos::fence("Bitset::deep_copy: fence after copy operation");
425 }
426 
427 template <typename DstDevice, typename SrcDevice>
428 void deep_copy(ConstBitset<DstDevice>& dst, ConstBitset<SrcDevice> const& src) {
429  if (dst.size() != src.size()) {
430  Kokkos::Impl::throw_runtime_exception(
431  "Error: Cannot deep_copy bitsets of different sizes!");
432  }
433 
434  Kokkos::fence("Bitset::deep_copy: fence before copy operation");
435  Kokkos::Impl::DeepCopy<typename DstDevice::memory_space,
436  typename SrcDevice::memory_space>(
437  dst.m_blocks.data(), src.m_blocks.data(),
438  sizeof(unsigned) * src.m_blocks.extent(0));
439  Kokkos::fence("Bitset::deep_copy: fence after copy operation");
440 }
441 
442 } // namespace Kokkos
443 
444 #ifdef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_BITSET
445 #undef KOKKOS_IMPL_PUBLIC_INCLUDE
446 #undef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_BITSET
447 #endif
448 #endif // KOKKOS_BITSET_HPP
A thread safe view to a bitset.
KOKKOS_FORCEINLINE_FUNCTION unsigned max_hint() const
Replacement for std::pair that works on CUDA devices.
Definition: Kokkos_Pair.hpp:44
Bitset(const Impl::ViewCtorProp< P...> &arg_prop, unsigned arg_size)
KOKKOS_FORCEINLINE_FUNCTION bool reset(unsigned i) const
unsigned count() const
KOKKOS_FORCEINLINE_FUNCTION bool test(unsigned i) const
KOKKOS_FORCEINLINE_FUNCTION unsigned size() const
KOKKOS_INLINE_FUNCTION Kokkos::pair< bool, unsigned > find_any_set_near(unsigned hint, unsigned scan_direction=BIT_SCAN_FORWARD_MOVE_HINT_FORWARD) const
Bitset(unsigned arg_size)
arg_size := number of bit in set
KOKKOS_FORCEINLINE_FUNCTION bool set(unsigned i) const
KOKKOS_INLINE_FUNCTION Kokkos::pair< bool, unsigned > find_any_unset_near(unsigned hint, unsigned scan_direction=BIT_SCAN_FORWARD_MOVE_HINT_FORWARD) const