Kokkos Core Kernels Package Version of the Day
Loading...
Searching...
No Matches
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
29namespace Kokkos {
30
31template <typename Device = Kokkos::DefaultExecutionSpace>
32class Bitset;
33
34template <typename Device = Kokkos::DefaultExecutionSpace>
35class ConstBitset;
36
37template <typename DstDevice, typename SrcDevice>
38void deep_copy(Bitset<DstDevice>& dst, Bitset<SrcDevice> const& src);
39
40template <typename DstDevice, typename SrcDevice>
41void deep_copy(Bitset<DstDevice>& dst, ConstBitset<SrcDevice> const& src);
42
43template <typename DstDevice, typename SrcDevice>
44void deep_copy(ConstBitset<DstDevice>& dst, ConstBitset<SrcDevice> const& src);
45
47template <typename Device>
48class 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
319template <typename Device>
320class 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
397template <typename DstDevice, typename SrcDevice>
398void 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
412template <typename DstDevice, typename SrcDevice>
413void 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
427template <typename DstDevice, typename SrcDevice>
428void 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.
Bitset(unsigned arg_size)
arg_size := number of bit in set
KOKKOS_FORCEINLINE_FUNCTION bool set(unsigned i) const
KOKKOS_FORCEINLINE_FUNCTION unsigned max_hint() const
Bitset(const Impl::ViewCtorProp< P... > &arg_prop, unsigned arg_size)
KOKKOS_FORCEINLINE_FUNCTION unsigned size() const
KOKKOS_INLINE_FUNCTION Kokkos::pair< bool, unsigned > find_any_unset_near(unsigned hint, unsigned scan_direction=BIT_SCAN_FORWARD_MOVE_HINT_FORWARD) const
unsigned count() const
KOKKOS_FORCEINLINE_FUNCTION bool test(unsigned i) const
KOKKOS_FORCEINLINE_FUNCTION bool reset(unsigned i) const
KOKKOS_INLINE_FUNCTION Kokkos::pair< bool, unsigned > find_any_set_near(unsigned hint, unsigned scan_direction=BIT_SCAN_FORWARD_MOVE_HINT_FORWARD) const
Replacement for std::pair that works on CUDA devices.