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;
233 typedef Device device_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.extent(0); }
494 KOKKOS_INLINE_FUNCTION
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.extent(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.extent(0));
518 size_type find_attempts = 0;
520 enum :
unsigned { 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.extent(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>
704 UnorderedMap( UnorderedMap<SKey,SValue,Device,Hasher,EqualTo>
const& src,
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
723 operator=( UnorderedMap<SKey,SValue,Device,Hasher,EqualTo>
const& src)
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
742 create_copy_view( UnorderedMap<SKey, SValue, SDevice, Hasher,EqualTo>
const& src)
744 if (m_hash_lists.data() != src.m_hash_lists.data()) {
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.extent(0) );
754 tmp.m_next_index = size_type_view( ViewAllocateWithoutInitializing(
"UnorderedMap next index"), src.m_next_index.extent(0) );
755 tmp.m_keys = key_type_view( ViewAllocateWithoutInitializing(
"UnorderedMap keys"), src.m_keys.extent(0) );
756 tmp.m_values = value_type_view( ViewAllocateWithoutInitializing(
"UnorderedMap values"), src.m_values.extent(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.data(), src.m_hash_lists.data(),
sizeof(size_type)*src.m_hash_lists.extent(0));
764 raw_deep_copy(tmp.m_next_index.data(), src.m_next_index.data(),
sizeof(size_type)*src.m_next_index.extent(0));
765 raw_deep_copy(tmp.m_keys.data(), src.m_keys.data(),
sizeof(key_type)*src.m_keys.extent(0));
767 raw_deep_copy(tmp.m_values.data(), src.m_values.data(),
sizeof(impl_value_type)*src.m_values.extent(0));
769 raw_deep_copy(tmp.m_scalars.data(), src.m_scalars.data(),
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.data() + 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.data() + 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.data() + 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>
824 friend class UnorderedMap;
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 >
840 inline void deep_copy( UnorderedMap<DKey, DT, DDevice, Hasher, EqualTo> & dst
841 ,
const UnorderedMap<SKey, ST, SDevice, Hasher, EqualTo> & src )
843 dst.create_copy_view(src);
849 #endif //KOKKOS_UNORDERED_MAP_HPP
KOKKOS_FORCEINLINE_FUNCTION bool success() const
Did the map successful insert the key/value pair.
A thread safe view to a bitset.
KOKKOS_FORCEINLINE_FUNCTION size_type capacity() const
The maximum number of entries that the table can hold.
KOKKOS_FORCEINLINE_FUNCTION uint32_t list_position() const
UnorderedMap(size_type capacity_hint, hasher_type hasher=hasher_type(), equal_to_type equal_to=equal_to_type())
Constructor.
KOKKOS_INLINE_FUNCTION insert_result insert(key_type const &k, impl_value_type const &v=impl_value_type()) const
size_type size() const
The number of entries in the table.
KOKKOS_FORCEINLINE_FUNCTION uint32_t index() const
Index where the key can be found as long as the insert did not fail.
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.
void clear()
Clear all entries in the table.
View to an array of data.
KOKKOS_INLINE_FUNCTION size_type hash_capacity() const
The number of hash table "buckets.".
First element of the return value of UnorderedMap::insert().
KOKKOS_FORCEINLINE_FUNCTION bool freed_existing() const
bool rehash(size_type requested_capacity=0)
Change the capacity of the the map.
KOKKOS_INLINE_FUNCTION bool exists(const key_type &k) const
Does the key exist in the map.
bool failed_insert() const
The current number of failed insert() calls.
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.
KOKKOS_FORCEINLINE_FUNCTION key_type key_at(size_type i) const
Get the key with i as its direct index.
KOKKOS_INLINE_FUNCTION size_type find(const key_type &k) const
Find the given key k, if it exists in the table.
Thread-safe, performance-portable lookup table.
KOKKOS_FORCEINLINE_FUNCTION bool failed() const
Did the map fail to insert the key due to insufficent capacity.
KOKKOS_FORCEINLINE_FUNCTION bool existing() const
Was the key already present in the map.
KOKKOS_FORCEINLINE_FUNCTION pair< T1 &, T2 & > tie(T1 &x, T2 &y)
Return a pair of references to the input arguments.