50 #ifndef KOKKOS_UNORDERED_MAP_HPP 51 #define KOKKOS_UNORDERED_MAP_HPP 53 #include <Kokkos_Core.hpp> 54 #include <Kokkos_Functional.hpp> 56 #include <Kokkos_Bitset.hpp> 58 #include <impl/Kokkos_Traits.hpp> 59 #include <impl/Kokkos_UnorderedMap_impl.hpp> 70 enum { UnorderedMapInvalidIndex = ~0u };
92 , FREED_EXISTING = 1u << 29
93 , LIST_LENGTH_MASK = ~(SUCCESS | EXISTING | FREED_EXISTING)
98 KOKKOS_FORCEINLINE_FUNCTION
99 bool success()
const {
return (m_status & SUCCESS); }
102 KOKKOS_FORCEINLINE_FUNCTION
103 bool existing()
const {
return (m_status & EXISTING); }
106 KOKKOS_FORCEINLINE_FUNCTION
107 bool failed()
const {
return m_index == UnorderedMapInvalidIndex; }
111 KOKKOS_FORCEINLINE_FUNCTION
116 KOKKOS_FORCEINLINE_FUNCTION
120 KOKKOS_FORCEINLINE_FUNCTION
121 uint32_t
index()
const {
return m_index; }
123 KOKKOS_FORCEINLINE_FUNCTION
125 : m_index(UnorderedMapInvalidIndex)
129 KOKKOS_FORCEINLINE_FUNCTION
130 void increment_list_position()
135 KOKKOS_FORCEINLINE_FUNCTION
136 void set_existing(uint32_t i,
bool arg_freed_existing)
139 m_status = EXISTING | (arg_freed_existing ? FREED_EXISTING : 0u) |
list_position();
142 KOKKOS_FORCEINLINE_FUNCTION
143 void set_success(uint32_t i)
209 template <
typename Key
211 ,
typename Device = Kokkos::DefaultExecutionSpace
212 ,
typename Hasher = pod_hash<typename Impl::remove_const<Key>::type>
213 ,
typename EqualTo = pod_equal_to<typename Impl::remove_const<Key>::type>
218 typedef typename ViewTraits<Key,Device,void,void>::host_mirror_space host_mirror_space ;
224 typedef Key declared_key_type;
225 typedef typename Impl::remove_const<declared_key_type>::type key_type;
226 typedef typename Impl::add_const<key_type>::type const_key_type;
229 typedef Value declared_value_type;
230 typedef typename Impl::remove_const<declared_value_type>::type value_type;
231 typedef typename Impl::add_const<value_type>::type const_value_type;
234 typedef typename Device::execution_space execution_space;
235 typedef Hasher hasher_type;
236 typedef EqualTo equal_to_type;
237 typedef uint32_t size_type;
245 static const bool is_set = std::is_same<void,value_type>::value;
246 static const bool has_const_key = std::is_same<const_key_type,declared_key_type>::value;
247 static const bool has_const_value = is_set || std::is_same<const_value_type,declared_value_type>::value;
249 static const bool is_insertable_map = !has_const_key && (is_set || !has_const_value);
250 static const bool is_modifiable_map = has_const_key && !has_const_value;
251 static const bool is_const_map = has_const_key && has_const_value;
258 typedef Impl::UnorderedMapHistogram<const_map_type> histogram_type;
263 enum { invalid_index = ~static_cast<size_type>(0) };
265 typedef typename Impl::if_c< is_set, int, declared_value_type>::type impl_value_type;
267 typedef typename Impl::if_c< is_insertable_map
270 >::type key_type_view;
272 typedef typename Impl::if_c< is_insertable_map || is_modifiable_map
275 >::type value_type_view;
277 typedef typename Impl::if_c< is_insertable_map
280 >::type size_type_view;
282 typedef typename Impl::if_c< is_insertable_map
287 enum { modified_idx = 0, erasable_idx = 1, failed_insert_idx = 2 };
288 enum { num_scalars = 3 };
300 , m_available_indexes()
313 UnorderedMap( size_type capacity_hint, hasher_type hasher = hasher_type(), equal_to_type equal_to = equal_to_type() )
314 : m_bounded_insert(true)
316 , m_equal_to(equal_to)
318 , m_available_indexes(calculate_capacity(capacity_hint))
319 , m_hash_lists(ViewAllocateWithoutInitializing(
"UnorderedMap hash list"), Impl::find_hash_size(capacity()))
320 , m_next_index(ViewAllocateWithoutInitializing(
"UnorderedMap next index"), capacity()+1)
321 , m_keys(
"UnorderedMap keys",capacity()+1)
322 , m_values(
"UnorderedMap values",(is_set? 1 : capacity()+1))
323 , m_scalars(
"UnorderedMap scalars")
325 if (!is_insertable_map) {
326 throw std::runtime_error(
"Cannot construct a non-insertable (i.e. const key_type) unordered_map");
333 void reset_failed_insert_flag()
335 reset_flag(failed_insert_idx);
338 histogram_type get_histogram()
340 return histogram_type(*
this);
346 m_bounded_insert =
true;
348 if (capacity() == 0)
return;
350 m_available_indexes.clear();
355 const key_type tmp = key_type();
359 const impl_value_type tmp = impl_value_type();
377 bool rehash(size_type requested_capacity = 0)
379 const bool bounded_insert = (capacity() == 0) || (size() == 0u);
380 return rehash(requested_capacity, bounded_insert );
383 bool rehash(size_type requested_capacity,
bool bounded_insert)
385 if(!is_insertable_map)
return false;
387 const size_type curr_size = size();
388 requested_capacity = (requested_capacity < curr_size) ? curr_size : requested_capacity;
390 insertable_map_type tmp(requested_capacity, m_hasher, m_equal_to);
393 tmp.m_bounded_insert =
false;
394 Impl::UnorderedMapRehash<insertable_map_type> f(tmp,*
this);
397 tmp.m_bounded_insert = bounded_insert;
413 if( capacity() == 0u )
return 0u;
415 m_size = m_available_indexes.count();
416 reset_flag(modified_idx);
428 return get_flag(failed_insert_idx);
431 bool erasable()
const 433 return is_insertable_map ? get_flag(erasable_idx) :
false;
438 bool result = !erasable();
439 if (is_insertable_map && result) {
440 execution_space::fence();
441 set_flag(erasable_idx);
442 execution_space::fence();
449 bool result = erasable();
450 if (is_insertable_map && result) {
451 execution_space::fence();
452 Impl::UnorderedMapErase<declared_map_type> f(*
this);
454 execution_space::fence();
455 reset_flag(erasable_idx);
464 KOKKOS_FORCEINLINE_FUNCTION
466 {
return m_available_indexes.size(); }
478 KOKKOS_INLINE_FUNCTION
480 {
return m_hash_lists.dimension_0(); }
494 KOKKOS_INLINE_FUNCTION
495 insert_result
insert(key_type
const& k, impl_value_type
const&v = impl_value_type())
const 497 insert_result result;
499 if ( !is_insertable_map || capacity() == 0u || m_scalars((
int)erasable_idx) ) {
503 if ( !m_scalars((
int)modified_idx) ) {
504 m_scalars((
int)modified_idx) =
true;
507 int volatile & failed_insert_ref = m_scalars((
int)failed_insert_idx) ;
509 const size_type hash_value = m_hasher(k);
510 const size_type hash_list = hash_value % m_hash_lists.dimension_0();
512 size_type * curr_ptr = & m_hash_lists[ hash_list ];
513 size_type new_index = invalid_index ;
516 size_type index_hint =
static_cast<size_type
>( (
static_cast<double>(hash_list) * capacity()) / m_hash_lists.dimension_0());
518 size_type find_attempts = 0;
520 enum { bounded_find_attempts = 32u };
521 const size_type max_attempts = (m_bounded_insert && (bounded_find_attempts < m_available_indexes.max_hint()) ) ?
522 bounded_find_attempts :
523 m_available_indexes.max_hint();
525 bool not_done = true ;
527 #if defined( __MIC__ ) 535 size_type curr = volatile_load(curr_ptr);
537 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(&m_keys[curr != invalid_index ? curr : 0]);
538 #if defined( __MIC__ ) 541 while ( curr != invalid_index && ! m_equal_to( volatile_load(&m_keys[curr]), k) ) {
542 result.increment_list_position();
544 curr_ptr = &m_next_index[curr];
545 curr = volatile_load(curr_ptr);
546 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(&m_keys[curr != invalid_index ? curr : 0]);
551 if ( curr != invalid_index ) {
553 const bool free_existing = new_index != invalid_index;
554 if ( free_existing ) {
557 if (!m_available_indexes.reset(new_index) ) {
558 printf(
"Unable to free existing\n");
563 result.set_existing(curr, free_existing);
573 if (new_index == invalid_index) {
577 Kokkos::tie(found, index_hint) = m_available_indexes.find_any_unset_near( index_hint, hash_list );
580 if ( !found && ++find_attempts >= max_attempts ) {
581 failed_insert_ref =
true;
584 else if (m_available_indexes.set(index_hint) ) {
585 new_index = index_hint;
587 KOKKOS_NONTEMPORAL_PREFETCH_STORE(&m_keys[new_index]);
588 m_keys[new_index] = k ;
591 KOKKOS_NONTEMPORAL_PREFETCH_STORE(&m_values[new_index]);
592 m_values[new_index] = v ;
599 else if (failed_insert_ref) {
605 if ( new_index != invalid_index &&
606 curr == atomic_compare_exchange(curr_ptr, static_cast<size_type>(invalid_index), new_index) ) {
608 result.set_success(new_index);
617 KOKKOS_INLINE_FUNCTION
618 bool erase(key_type
const& k)
const 622 if(is_insertable_map && 0u < capacity() && m_scalars((
int)erasable_idx)) {
624 if ( ! m_scalars((
int)modified_idx) ) {
625 m_scalars((
int)modified_idx) =
true;
628 size_type
index = find(k);
629 if (valid_at(index)) {
630 m_available_indexes.reset(index);
645 KOKKOS_INLINE_FUNCTION
646 size_type
find(
const key_type & k)
const 648 size_type curr = 0u < capacity() ? m_hash_lists( m_hasher(k) % m_hash_lists.dimension_0() ) : invalid_index ;
650 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(&m_keys[curr != invalid_index ? curr : 0]);
651 while (curr != invalid_index && !m_equal_to( m_keys[curr], k) ) {
652 KOKKOS_NONTEMPORAL_PREFETCH_LOAD(&m_keys[curr != invalid_index ? curr : 0]);
653 curr = m_next_index[curr];
663 KOKKOS_INLINE_FUNCTION
666 return valid_at(find(k));
678 KOKKOS_FORCEINLINE_FUNCTION
679 typename Impl::if_c< (is_set || has_const_value), impl_value_type, impl_value_type &>::type
682 return m_values[ is_set ? 0 : (i < capacity() ? i : capacity()) ];
691 KOKKOS_FORCEINLINE_FUNCTION
694 return m_keys[ i < capacity() ? i : capacity() ];
697 KOKKOS_FORCEINLINE_FUNCTION
698 bool valid_at(size_type i)
const 700 return m_available_indexes.test(i);
703 template <
typename SKey,
typename SValue>
705 typename Impl::enable_if< Impl::UnorderedMapCanAssign<declared_key_type,declared_value_type,SKey,SValue>::value,
int>::type = 0
707 : m_bounded_insert(src.m_bounded_insert)
708 , m_hasher(src.m_hasher)
709 , m_equal_to(src.m_equal_to)
711 , m_available_indexes(src.m_available_indexes)
712 , m_hash_lists(src.m_hash_lists)
713 , m_next_index(src.m_next_index)
715 , m_values(src.m_values)
716 , m_scalars(src.m_scalars)
720 template <
typename SKey,
typename SValue>
721 typename Impl::enable_if< Impl::UnorderedMapCanAssign<declared_key_type,declared_value_type,SKey,SValue>::value
722 ,declared_map_type & >::type
725 m_bounded_insert = src.m_bounded_insert;
726 m_hasher = src.m_hasher;
727 m_equal_to = src.m_equal_to;
729 m_available_indexes = src.m_available_indexes;
730 m_hash_lists = src.m_hash_lists;
731 m_next_index = src.m_next_index;
733 m_values = src.m_values;
734 m_scalars = src.m_scalars;
738 template <
typename SKey,
typename SValue,
typename SDevice>
739 typename Impl::enable_if< std::is_same< typename Impl::remove_const<SKey>::type, key_type>::value &&
740 std::is_same< typename Impl::remove_const<SValue>::type, value_type>::value
744 if (m_hash_lists.ptr_on_device() != src.m_hash_lists.ptr_on_device()) {
746 insertable_map_type tmp;
748 tmp.m_bounded_insert = src.m_bounded_insert;
749 tmp.m_hasher = src.m_hasher;
750 tmp.m_equal_to = src.m_equal_to;
751 tmp.m_size = src.
size();
752 tmp.m_available_indexes = bitset_type( src.
capacity() );
753 tmp.m_hash_lists = size_type_view( ViewAllocateWithoutInitializing(
"UnorderedMap hash list"), src.m_hash_lists.dimension_0() );
754 tmp.m_next_index = size_type_view( ViewAllocateWithoutInitializing(
"UnorderedMap next index"), src.m_next_index.dimension_0() );
755 tmp.m_keys = key_type_view( ViewAllocateWithoutInitializing(
"UnorderedMap keys"), src.m_keys.dimension_0() );
756 tmp.m_values = value_type_view( ViewAllocateWithoutInitializing(
"UnorderedMap values"), src.m_values.dimension_0() );
757 tmp.m_scalars = scalars_view(
"UnorderedMap scalars");
761 typedef Kokkos::Impl::DeepCopy< typename device_type::memory_space, typename SDevice::memory_space > raw_deep_copy;
763 raw_deep_copy(tmp.m_hash_lists.ptr_on_device(), src.m_hash_lists.ptr_on_device(),
sizeof(size_type)*src.m_hash_lists.dimension_0());
764 raw_deep_copy(tmp.m_next_index.ptr_on_device(), src.m_next_index.ptr_on_device(),
sizeof(size_type)*src.m_next_index.dimension_0());
765 raw_deep_copy(tmp.m_keys.ptr_on_device(), src.m_keys.ptr_on_device(),
sizeof(key_type)*src.m_keys.dimension_0());
767 raw_deep_copy(tmp.m_values.ptr_on_device(), src.m_values.ptr_on_device(),
sizeof(impl_value_type)*src.m_values.dimension_0());
769 raw_deep_copy(tmp.m_scalars.ptr_on_device(), src.m_scalars.ptr_on_device(),
sizeof(int)*num_scalars );
778 bool modified()
const 780 return get_flag(modified_idx);
783 void set_flag(
int flag)
const 785 typedef Kokkos::Impl::DeepCopy< typename device_type::memory_space, Kokkos::HostSpace > raw_deep_copy;
786 const int true_ =
true;
787 raw_deep_copy(m_scalars.ptr_on_device() + flag, &true_,
sizeof(int));
790 void reset_flag(
int flag)
const 792 typedef Kokkos::Impl::DeepCopy< typename device_type::memory_space, Kokkos::HostSpace > raw_deep_copy;
793 const int false_ =
false;
794 raw_deep_copy(m_scalars.ptr_on_device() + flag, &false_,
sizeof(int));
797 bool get_flag(
int flag)
const 799 typedef Kokkos::Impl::DeepCopy< Kokkos::HostSpace, typename device_type::memory_space > raw_deep_copy;
801 raw_deep_copy(&result, m_scalars.ptr_on_device() + flag,
sizeof(int));
805 static uint32_t calculate_capacity(uint32_t capacity_hint)
808 return capacity_hint ? ((
static_cast<uint32_t
>(7ull*capacity_hint/6u) + 127u)/128u)*128u : 128u;
812 bool m_bounded_insert;
813 hasher_type m_hasher;
814 equal_to_type m_equal_to;
815 mutable size_type m_size;
816 bitset_type m_available_indexes;
817 size_type_view m_hash_lists;
818 size_type_view m_next_index;
819 key_type_view m_keys;
820 value_type_view m_values;
821 scalars_view m_scalars;
823 template <
typename KKey,
typename VValue,
typename DDevice,
typename HHash,
typename EEqualTo>
826 template <
typename UMap>
827 friend struct Impl::UnorderedMapErase;
829 template <
typename UMap>
830 friend struct Impl::UnorderedMapHistogram;
832 template <
typename UMap>
833 friend struct Impl::UnorderedMapPrint;
837 template <
typename DKey,
typename DT,
typename DDevice
838 ,
typename SKey,
typename ST,
typename SDevice
839 ,
typename Hasher,
typename EqualTo >
843 dst.create_copy_view(src);
849 #endif //KOKKOS_UNORDERED_MAP_HPP A thread safe view to a bitset.
KOKKOS_FORCEINLINE_FUNCTION key_type key_at(size_type i) const
Get the key with i as its direct index.
KOKKOS_FORCEINLINE_FUNCTION bool success() const
Did the map successful insert the key/value pair.
bool failed_insert() const
The current number of failed insert() calls.
UnorderedMap(size_type capacity_hint, hasher_type hasher=hasher_type(), equal_to_type equal_to=equal_to_type())
Constructor.
KOKKOS_FORCEINLINE_FUNCTION bool existing() const
Was the key already present in the map.
KOKKOS_FORCEINLINE_FUNCTION bool failed() const
Did the map fail to insert the key due to insufficent capacity.
KOKKOS_INLINE_FUNCTION size_type find(const key_type &k) const
Find the given key k, if it exists in the table.
KOKKOS_FORCEINLINE_FUNCTION size_type capacity() const
The maximum number of entries that the table can hold.
KOKKOS_FORCEINLINE_FUNCTION Impl::if_c<(is_set||has_const_value), impl_value_type, impl_value_type & >::type value_at(size_type i) const
Get the value with i as its direct index.
void clear()
Clear all entries in the table.
View to an array of data.
Memory space for main process and CPU execution spaces.
First element of the return value of UnorderedMap::insert().
bool rehash(size_type requested_capacity=0)
Change the capacity of the the map.
size_type size() const
The number of entries in the table.
KOKKOS_INLINE_FUNCTION insert_result insert(key_type const &k, impl_value_type const &v=impl_value_type()) const
void deep_copy(const View< DT, DP... > &dst, typename ViewTraits< DT, DP... >::const_value_type &value, typename std::enable_if< std::is_same< typename ViewTraits< DT, DP... >::specialize, void >::value >::type *=0)
Deep copy a value from Host memory into a view.
KOKKOS_INLINE_FUNCTION size_type hash_capacity() const
The number of hash table "buckets.".
KOKKOS_FORCEINLINE_FUNCTION uint32_t list_position() const
KOKKOS_FORCEINLINE_FUNCTION uint32_t index() const
Index where the key can be found as long as the insert did not fail.
KOKKOS_INLINE_FUNCTION bool exists(const key_type &k) const
Does the key exist in the map.
Thread-safe, performance-portable lookup table.
KOKKOS_FORCEINLINE_FUNCTION pair< T1 &, T2 & > tie(T1 &x, T2 &y)
Return a pair of references to the input arguments.
KOKKOS_FORCEINLINE_FUNCTION bool freed_existing() const