213 using host_mirror_space =
214 typename ViewTraits<Key, Device, void, void>::host_mirror_space;
220 using declared_key_type = Key;
221 using key_type = std::remove_const_t<declared_key_type>;
222 using const_key_type = std::add_const_t<key_type>;
225 using declared_value_type = Value;
226 using value_type = std::remove_const_t<declared_value_type>;
227 using const_value_type = std::add_const_t<value_type>;
229 using device_type = Device;
230 using execution_space =
typename Device::execution_space;
231 using hasher_type = Hasher;
232 using equal_to_type = EqualTo;
233 using size_type = uint32_t;
236 using declared_map_type =
237 UnorderedMap<declared_key_type, declared_value_type, device_type,
238 hasher_type, equal_to_type>;
239 using insertable_map_type = UnorderedMap<key_type, value_type, device_type,
240 hasher_type, equal_to_type>;
241 using modifiable_map_type =
242 UnorderedMap<const_key_type, value_type, device_type, hasher_type,
244 using const_map_type = UnorderedMap<const_key_type, const_value_type,
245 device_type, hasher_type, equal_to_type>;
247 static const bool is_set = std::is_void<value_type>::value;
248 static const bool has_const_key =
249 std::is_same<const_key_type, declared_key_type>::value;
250 static const bool has_const_value =
251 is_set || std::is_same<const_value_type, declared_value_type>::value;
253 static const bool is_insertable_map =
254 !has_const_key && (is_set || !has_const_value);
255 static const bool is_modifiable_map = has_const_key && !has_const_value;
256 static const bool is_const_map = has_const_key && has_const_value;
261 UnorderedMap<Key, Value, host_mirror_space, Hasher, EqualTo>;
263 using histogram_type = Impl::UnorderedMapHistogram<const_map_type>;
267 enum : size_type { invalid_index = ~static_cast<size_type>(0) };
269 using impl_value_type = std::conditional_t<is_set, int, declared_value_type>;
271 using key_type_view = std::conditional_t<
275 using value_type_view = std::conditional_t<
276 is_insertable_map || is_modifiable_map,
280 using size_type_view = std::conditional_t<
284 using bitset_type = std::conditional_t<is_insertable_map, Bitset<Device>,
287 enum { modified_idx = 0, erasable_idx = 1, failed_insert_idx = 2 };
288 enum { num_scalars = 3 };
294 using default_op_type =
295 typename UnorderedMapInsertOpTypes<value_type_view, uint32_t>::NoOp;
305 UnorderedMap(size_type capacity_hint = 0, hasher_type hasher = hasher_type(),
306 equal_to_type equal_to = equal_to_type())
307 : m_bounded_insert(true),
309 m_equal_to(equal_to),
310 m_size(
"UnorderedMap size"),
311 m_available_indexes(calculate_capacity(capacity_hint)),
312 m_hash_lists(view_alloc(WithoutInitializing,
"UnorderedMap hash list"),
314 m_next_index(view_alloc(WithoutInitializing,
"UnorderedMap next index"),
318 m_keys(
"UnorderedMap keys",
capacity()),
319 m_values(
"UnorderedMap values", (is_set ? 0 :
capacity())),
320 m_scalars(
"UnorderedMap scalars") {
321 if (!is_insertable_map) {
322 Kokkos::Impl::throw_runtime_exception(
323 "Cannot construct a non-insertable (i.e. const key_type) "
327 Kokkos::deep_copy(m_hash_lists, invalid_index);
328 Kokkos::deep_copy(m_next_index, invalid_index);
331 void reset_failed_insert_flag() { reset_flag(failed_insert_idx); }
333 histogram_type get_histogram() {
return histogram_type(*
this); }
337 m_bounded_insert =
true;
341 m_available_indexes.clear();
343 Kokkos::deep_copy(m_hash_lists, invalid_index);
344 Kokkos::deep_copy(m_next_index, invalid_index);
346 const key_type tmp = key_type();
347 Kokkos::deep_copy(m_keys, tmp);
349 Kokkos::deep_copy(m_scalars, 0);
353 KOKKOS_INLINE_FUNCTION
constexpr bool is_allocated()
const {
354 return (m_keys.is_allocated() && (is_set || m_values.is_allocated()) &&
355 m_scalars.is_allocated());
368 bool rehash(size_type requested_capacity = 0) {
369 const bool bounded_insert = (
capacity() == 0) || (
size() == 0u);
370 return rehash(requested_capacity, bounded_insert);
373 bool rehash(size_type requested_capacity,
bool bounded_insert) {
374 if (!is_insertable_map)
return false;
376 const size_type curr_size =
size();
378 (requested_capacity < curr_size) ? curr_size : requested_capacity;
380 insertable_map_type tmp(requested_capacity, m_hasher, m_equal_to);
383 tmp.m_bounded_insert =
false;
384 Impl::UnorderedMapRehash<insertable_map_type> f(tmp, *
this);
387 tmp.m_bounded_insert = bounded_insert;
404 m_size() = m_available_indexes.count();
405 reset_flag(modified_idx);
417 bool erasable()
const {
418 return is_insertable_map ? get_flag(erasable_idx) : false;
422 bool result = !erasable();
423 if (is_insertable_map && result) {
424 execution_space().fence(
425 "Kokkos::UnorderedMap::begin_erase: fence before setting erasable "
427 set_flag(erasable_idx);
433 bool result = erasable();
434 if (is_insertable_map && result) {
435 execution_space().fence(
436 "Kokkos::UnorderedMap::end_erase: fence before erasing");
437 Impl::UnorderedMapErase<declared_map_type> f(*
this);
439 execution_space().fence(
440 "Kokkos::UnorderedMap::end_erase: fence after erasing");
441 reset_flag(erasable_idx);
450 KOKKOS_FORCEINLINE_FUNCTION
451 size_type
capacity()
const {
return m_available_indexes.size(); }
463 KOKKOS_INLINE_FUNCTION
480 template <
typename InsertOpType = default_op_type>
481 KOKKOS_INLINE_FUNCTION insert_result
482 insert(key_type
const &k, impl_value_type
const &v = impl_value_type(),
483 [[maybe_unused]] InsertOpType arg_insert_op = InsertOpType())
const {
484 if constexpr (is_set) {
485 static_assert(std::is_same_v<InsertOpType, default_op_type>,
486 "Insert Operations are not supported on sets.");
489 insert_result result;
491 if (!is_insertable_map ||
capacity() == 0u ||
492 m_scalars((
int)erasable_idx)) {
496 if (!m_scalars((
int)modified_idx)) {
497 m_scalars((
int)modified_idx) =
true;
500 int volatile &failed_insert_ref = m_scalars((
int)failed_insert_idx);
502 const size_type hash_value = m_hasher(k);
503 const size_type hash_list = hash_value % m_hash_lists.extent(0);
505 size_type *curr_ptr = &m_hash_lists[hash_list];
506 size_type new_index = invalid_index;
509 size_type index_hint =
static_cast<size_type
>(
510 (
static_cast<double>(hash_list) *
capacity()) / m_hash_lists.extent(0));
512 size_type find_attempts = 0;
514 enum :
unsigned { bounded_find_attempts = 32u };
515 const size_type max_attempts =
517 (bounded_find_attempts < m_available_indexes.max_hint()))
518 ? bounded_find_attempts
519 : m_available_indexes.max_hint();
521 bool not_done =
true;
532#ifdef KOKKOS_ENABLE_SYCL
533 size_type curr = Kokkos::atomic_load(curr_ptr);
535 size_type curr = volatile_load(curr_ptr);
538 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(
539 &m_keys[curr != invalid_index ? curr : 0]);
543 while (curr != invalid_index && !m_equal_to(
544#ifdef KOKKOS_ENABLE_SYCL
545 Kokkos::atomic_load(&m_keys[curr])
547 volatile_load(&m_keys[curr])
551 result.increment_list_position();
553 curr_ptr = &m_next_index[curr];
554#ifdef KOKKOS_ENABLE_SYCL
555 curr = Kokkos::atomic_load(curr_ptr);
557 curr = volatile_load(curr_ptr);
559 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(
560 &m_keys[curr != invalid_index ? curr : 0]);
565 if (curr != invalid_index) {
566 const bool free_existing = new_index != invalid_index;
570 if (!m_available_indexes.reset(new_index)) {
571 KOKKOS_IMPL_DO_NOT_USE_PRINTF(
"Unable to free existing\n");
575 result.set_existing(curr, free_existing);
576 if constexpr (!is_set) {
577 arg_insert_op.op(m_values, curr, v);
587 if (new_index == invalid_index) {
591 m_available_indexes.find_any_unset_near(index_hint, hash_list);
594 if (!found && ++find_attempts >= max_attempts) {
595 failed_insert_ref =
true;
597 }
else if (m_available_indexes.set(index_hint)) {
598 new_index = index_hint;
600 KOKKOS_NONTEMPORAL_PREFETCH_STORE(&m_keys[new_index]);
602#ifdef KOKKOS_ENABLE_SYCL
603 Kokkos::atomic_store(&m_keys[new_index], k);
605 m_keys[new_index] = k;
609 KOKKOS_NONTEMPORAL_PREFETCH_STORE(&m_values[new_index]);
610#ifdef KOKKOS_ENABLE_SYCL
611 Kokkos::atomic_store(&m_values[new_index], v);
613 m_values[new_index] = v;
617#ifndef KOKKOS_ENABLE_SYCL
622 }
else if (failed_insert_ref) {
629 if (new_index != invalid_index &&
630 curr == atomic_compare_exchange(
631 curr_ptr,
static_cast<size_type
>(invalid_index),
634 result.set_success(new_index);
643 KOKKOS_INLINE_FUNCTION
644 bool erase(key_type
const &k)
const {
647 if (is_insertable_map && 0u <
capacity() && m_scalars((
int)erasable_idx)) {
648 if (!m_scalars((
int)modified_idx)) {
649 m_scalars((
int)modified_idx) =
true;
652 size_type index =
find(k);
653 if (valid_at(index)) {
654 m_available_indexes.reset(index);
669 KOKKOS_INLINE_FUNCTION
670 size_type
find(
const key_type &k)
const {
672 ? m_hash_lists(m_hasher(k) % m_hash_lists.extent(0))
675 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(&m_keys[curr != invalid_index ? curr : 0]);
676 while (curr != invalid_index && !m_equal_to(m_keys[curr], k)) {
677 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(
678 &m_keys[curr != invalid_index ? curr : 0]);
679 curr = m_next_index[curr];
689 KOKKOS_INLINE_FUNCTION
690 bool exists(
const key_type &k)
const {
return valid_at(
find(k)); }
700 template <
typename Dummy = value_type>
701 KOKKOS_FORCEINLINE_FUNCTION std::enable_if_t<
702 !std::is_void<Dummy>::value,
703 std::conditional_t<has_const_value, impl_value_type, impl_value_type &>>
715 KOKKOS_FORCEINLINE_FUNCTION
721 KOKKOS_FORCEINLINE_FUNCTION
722 bool valid_at(size_type i)
const {
return m_available_indexes.test(i); }
724 template <
typename SKey,
typename SValue>
726 UnorderedMap<SKey, SValue, Device, Hasher, EqualTo>
const &src,
728 Impl::UnorderedMapCanAssign<declared_key_type, declared_value_type,
729 SKey, SValue>::value,
731 : m_bounded_insert(src.m_bounded_insert),
732 m_hasher(src.m_hasher),
733 m_equal_to(src.m_equal_to),
735 m_available_indexes(src.m_available_indexes),
736 m_hash_lists(src.m_hash_lists),
737 m_next_index(src.m_next_index),
739 m_values(src.m_values),
740 m_scalars(src.m_scalars) {}
742 template <
typename SKey,
typename SValue>
744 Impl::UnorderedMapCanAssign<declared_key_type, declared_value_type, SKey,
747 operator=(UnorderedMap<SKey, SValue, Device, Hasher, EqualTo>
const &src) {
748 m_bounded_insert = src.m_bounded_insert;
749 m_hasher = src.m_hasher;
750 m_equal_to = src.m_equal_to;
752 m_available_indexes = src.m_available_indexes;
753 m_hash_lists = src.m_hash_lists;
754 m_next_index = src.m_next_index;
756 m_values = src.m_values;
757 m_scalars = src.m_scalars;
761 template <
typename SKey,
typename SValue,
typename SDevice>
762 std::enable_if_t<std::is_same<std::remove_const_t<SKey>, key_type>::value &&
763 std::is_same<std::remove_const_t<SValue>, value_type>::value>
765 UnorderedMap<SKey, SValue, SDevice, Hasher, EqualTo>
const &src) {
766 if (m_hash_lists.data() != src.m_hash_lists.data()) {
767 insertable_map_type tmp;
769 tmp.m_bounded_insert = src.m_bounded_insert;
770 tmp.m_hasher = src.m_hasher;
771 tmp.m_equal_to = src.m_equal_to;
772 tmp.m_size() = src.m_size();
773 tmp.m_available_indexes = bitset_type(src.capacity());
774 tmp.m_hash_lists = size_type_view(
775 view_alloc(WithoutInitializing,
"UnorderedMap hash list"),
776 src.m_hash_lists.extent(0));
777 tmp.m_next_index = size_type_view(
778 view_alloc(WithoutInitializing,
"UnorderedMap next index"),
779 src.m_next_index.extent(0));
781 key_type_view(view_alloc(WithoutInitializing,
"UnorderedMap keys"),
782 src.m_keys.extent(0));
783 tmp.m_values = value_type_view(
784 view_alloc(WithoutInitializing,
"UnorderedMap values"),
785 src.m_values.extent(0));
786 tmp.m_scalars = scalars_view(
"UnorderedMap scalars");
788 Kokkos::deep_copy(tmp.m_available_indexes, src.m_available_indexes);
790 using raw_deep_copy =
791 Kokkos::Impl::DeepCopy<
typename device_type::memory_space,
792 typename SDevice::memory_space>;
794 raw_deep_copy(tmp.m_hash_lists.data(), src.m_hash_lists.data(),
795 sizeof(size_type) * src.m_hash_lists.extent(0));
796 raw_deep_copy(tmp.m_next_index.data(), src.m_next_index.data(),
797 sizeof(size_type) * src.m_next_index.extent(0));
798 raw_deep_copy(tmp.m_keys.data(), src.m_keys.data(),
799 sizeof(key_type) * src.m_keys.extent(0));
801 raw_deep_copy(tmp.m_values.data(), src.m_values.data(),
802 sizeof(impl_value_type) * src.m_values.extent(0));
804 raw_deep_copy(tmp.m_scalars.data(), src.m_scalars.data(),
805 sizeof(
int) * num_scalars);
808 "Kokkos::UnorderedMap::create_copy_view: fence after copy to tmp");
816 bool modified()
const {
return get_flag(modified_idx); }
818 void set_flag(
int flag)
const {
819 using raw_deep_copy =
820 Kokkos::Impl::DeepCopy<
typename device_type::memory_space,
822 const int true_ =
true;
823 raw_deep_copy(m_scalars.data() + flag, &true_,
sizeof(
int));
825 "Kokkos::UnorderedMap::set_flag: fence after copying flag from "
829 void reset_flag(
int flag)
const {
830 using raw_deep_copy =
831 Kokkos::Impl::DeepCopy<
typename device_type::memory_space,
833 const int false_ =
false;
834 raw_deep_copy(m_scalars.data() + flag, &false_,
sizeof(
int));
836 "Kokkos::UnorderedMap::reset_flag: fence after copying flag from "
840 bool get_flag(
int flag)
const {
841 using raw_deep_copy =
842 Kokkos::Impl::DeepCopy<Kokkos::HostSpace,
843 typename device_type::memory_space>;
845 raw_deep_copy(&result, m_scalars.data() + flag,
sizeof(
int));
847 "Kokkos::UnorderedMap::get_flag: fence after copy to return value in "
852 static uint32_t calculate_capacity(uint32_t capacity_hint) {
855 ? ((
static_cast<uint32_t
>(7ull * capacity_hint / 6u) + 127u) /
862 bool m_bounded_insert;
863 hasher_type m_hasher;
864 equal_to_type m_equal_to;
865 View<size_type, HostSpace> m_size;
866 bitset_type m_available_indexes;
867 size_type_view m_hash_lists;
868 size_type_view m_next_index;
869 key_type_view m_keys;
870 value_type_view m_values;
871 scalars_view m_scalars;
873 template <
typename KKey,
typename VValue,
typename DDevice,
typename HHash,
875 friend class UnorderedMap;
877 template <
typename UMap>
878 friend struct Impl::UnorderedMapErase;
880 template <
typename UMap>
881 friend struct Impl::UnorderedMapHistogram;
883 template <
typename UMap>
884 friend struct Impl::UnorderedMapPrint;