@@ -962,6 +962,164 @@ class open_addressing_ref_impl {
962962 }
963963 }
964964
965+ /* *
966+ * @brief Executes a callback on every element in the container with key equivalent to the probe
967+ * key.
968+ *
969+ * @note Passes an un-incrementable input iterator to the element whose key is equivalent to
970+ * `key` to the callback.
971+ *
972+ * @tparam ProbeKey Input type which is convertible to 'key_type'
973+ * @tparam CallbackOp Unary callback functor or device lambda
974+ *
975+ * @param key The key to search for
976+ * @param callback_op Function to call on every element found
977+ */
978+ template <class ProbeKey , class CallbackOp >
979+ __device__ void for_each (ProbeKey const & key, CallbackOp&& callback_op) const noexcept
980+ {
981+ static_assert (cg_size == 1 , " Non-CG operation is incompatible with the current probing scheme" );
982+ auto probing_iter = this ->probing_scheme_ (key, this ->storage_ref_ .window_extent ());
983+
984+ while (true ) {
985+ // TODO atomic_ref::load if insert operator is present
986+ auto const window_slots = this ->storage_ref_ [*probing_iter];
987+
988+ for (int32_t i = 0 ; i < window_size; ++i) {
989+ switch (
990+ this ->predicate_ .operator ()<is_insert::NO>(key, this ->extract_key (window_slots[i]))) {
991+ case detail::equal_result::EMPTY: {
992+ return ;
993+ }
994+ case detail::equal_result::EQUAL: {
995+ callback_op (const_iterator{&(*(this ->storage_ref_ .data () + *probing_iter))[i]});
996+ continue ;
997+ }
998+ default : continue ;
999+ }
1000+ }
1001+ ++probing_iter;
1002+ }
1003+ }
1004+
1005+ /* *
1006+ * @brief Executes a callback on every element in the container with key equivalent to the probe
1007+ * key.
1008+ *
1009+ * @note Passes an un-incrementable input iterator to the element whose key is equivalent to
1010+ * `key` to the callback.
1011+ *
1012+ * @note This function uses cooperative group semantics, meaning that any thread may call the
1013+ * callback if it finds a matching element. If multiple elements are found within the same group,
1014+ * each thread with a match will call the callback with its associated element.
1015+ *
1016+ * @note Synchronizing `group` within `callback_op` is undefined behavior.
1017+ *
1018+ * @tparam ProbeKey Input type which is convertible to 'key_type'
1019+ * @tparam CallbackOp Unary callback functor or device lambda
1020+ *
1021+ * @param group The Cooperative Group used to perform this operation
1022+ * @param key The key to search for
1023+ * @param callback_op Function to call on every element found
1024+ */
1025+ template <class ProbeKey , class CallbackOp >
1026+ __device__ void for_each (cooperative_groups::thread_block_tile<cg_size> const & group,
1027+ ProbeKey const & key,
1028+ CallbackOp&& callback_op) const noexcept
1029+ {
1030+ auto probing_iter = this ->probing_scheme_ (group, key, this ->storage_ref_ .window_extent ());
1031+ bool empty = false ;
1032+
1033+ while (true ) {
1034+ // TODO atomic_ref::load if insert operator is present
1035+ auto const window_slots = this ->storage_ref_ [*probing_iter];
1036+
1037+ for (int32_t i = 0 ; i < window_size and !empty; ++i) {
1038+ switch (
1039+ this ->predicate_ .operator ()<is_insert::NO>(key, this ->extract_key (window_slots[i]))) {
1040+ case detail::equal_result::EMPTY: {
1041+ empty = true ;
1042+ continue ;
1043+ }
1044+ case detail::equal_result::EQUAL: {
1045+ callback_op (const_iterator{&(*(this ->storage_ref_ .data () + *probing_iter))[i]});
1046+ continue ;
1047+ }
1048+ default : {
1049+ continue ;
1050+ }
1051+ }
1052+ }
1053+ if (group.any (empty)) { return ; }
1054+
1055+ ++probing_iter;
1056+ }
1057+ }
1058+
1059+ /* *
1060+ * @brief Executes a callback on every element in the container with key equivalent to the probe
1061+ * key and can additionally perform work that requires synchronizing the Cooperative Group
1062+ * performing this operation.
1063+ *
1064+ * @note Passes an un-incrementable input iterator to the element whose key is equivalent to
1065+ * `key` to the callback.
1066+ *
1067+ * @note This function uses cooperative group semantics, meaning that any thread may call the
1068+ * callback if it finds a matching element. If multiple elements are found within the same group,
1069+ * each thread with a match will call the callback with its associated element.
1070+ *
1071+ * @note Synchronizing `group` within `callback_op` is undefined behavior.
1072+ *
1073+ * @note The `sync_op` function can be used to perform work that requires synchronizing threads in
1074+ * `group` inbetween probing steps, where the number of probing steps performed between
1075+ * synchronization points is capped by `window_size * cg_size`. The functor will be called right
1076+ * after the current probing window has been traversed.
1077+ *
1078+ * @tparam ProbeKey Input type which is convertible to 'key_type'
1079+ * @tparam CallbackOp Unary callback functor or device lambda
1080+ * @tparam SyncOp Functor or device lambda which accepts the current `group` object
1081+ *
1082+ * @param group The Cooperative Group used to perform this operation
1083+ * @param key The key to search for
1084+ * @param callback_op Function to call on every element found
1085+ * @param sync_op Function that is allowed to synchronize `group` inbetween probing windows
1086+ */
1087+ template <class ProbeKey , class CallbackOp , class SyncOp >
1088+ __device__ void for_each (cooperative_groups::thread_block_tile<cg_size> const & group,
1089+ ProbeKey const & key,
1090+ CallbackOp&& callback_op,
1091+ SyncOp&& sync_op) const noexcept
1092+ {
1093+ auto probing_iter = this ->probing_scheme_ (group, key, this ->storage_ref_ .window_extent ());
1094+ bool empty = false ;
1095+
1096+ while (true ) {
1097+ // TODO atomic_ref::load if insert operator is present
1098+ auto const window_slots = this ->storage_ref_ [*probing_iter];
1099+
1100+ for (int32_t i = 0 ; i < window_size and !empty; ++i) {
1101+ switch (
1102+ this ->predicate_ .operator ()<is_insert::NO>(key, this ->extract_key (window_slots[i]))) {
1103+ case detail::equal_result::EMPTY: {
1104+ empty = true ;
1105+ continue ;
1106+ }
1107+ case detail::equal_result::EQUAL: {
1108+ callback_op (const_iterator{&(*(this ->storage_ref_ .data () + *probing_iter))[i]});
1109+ continue ;
1110+ }
1111+ default : {
1112+ continue ;
1113+ }
1114+ }
1115+ }
1116+ sync_op (group);
1117+ if (group.any (empty)) { return ; }
1118+
1119+ ++probing_iter;
1120+ }
1121+ }
1122+
9651123 /* *
9661124 * @brief Compares the content of the address `address` (old value) with the `expected` value and,
9671125 * only if they are the same, sets the content of `address` to `desired`.
0 commit comments