1 //@HEADER
2 // ************************************************************************
3 //
4 // Kokkos v. 3.0
5 // Copyright (2020) 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 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Christian R. Trott (crtrott@sandia.gov)
39 //
40 // ************************************************************************
41 //@HEADER
42
43 #ifndef KOKKOS_TEST_UNORDERED_MAP_HPP
44 #define KOKKOS_TEST_UNORDERED_MAP_HPP
45
46 #include <gtest/gtest.h>
47 #include <iostream>
48 #include <Kokkos_UnorderedMap.hpp>
49
50 namespace Test {
51
52 namespace Impl {
53
54 template <typename MapType, bool Near = false>
55 struct TestInsert {
56 using map_type = MapType;
57 using execution_space = typename map_type::execution_space;
58 using value_type = uint32_t;
59
60 map_type map;
61 uint32_t inserts;
62 uint32_t collisions;
63
TestInsertTest::Impl::TestInsert64 TestInsert(map_type arg_map, uint32_t arg_inserts, uint32_t arg_collisions)
65 : map(arg_map), inserts(arg_inserts), collisions(arg_collisions) {}
66
testitTest::Impl::TestInsert67 void testit(bool rehash_on_fail = true) {
68 execution_space().fence();
69
70 uint32_t failed_count = 0;
71 do {
72 failed_count = 0;
73 Kokkos::parallel_reduce(inserts, *this, failed_count);
74
75 if (rehash_on_fail && failed_count > 0u) {
76 const uint32_t new_capacity = map.capacity() +
77 ((map.capacity() * 3ull) / 20u) +
78 failed_count / collisions;
79 map.rehash(new_capacity);
80 }
81 } while (rehash_on_fail && failed_count > 0u);
82
83 execution_space().fence();
84 }
85
86 KOKKOS_INLINE_FUNCTION
initTest::Impl::TestInsert87 void init(value_type &failed_count) const { failed_count = 0; }
88
89 KOKKOS_INLINE_FUNCTION
joinTest::Impl::TestInsert90 void join(volatile value_type &failed_count,
91 const volatile value_type &count) const {
92 failed_count += count;
93 }
94
95 KOKKOS_INLINE_FUNCTION
operator ()Test::Impl::TestInsert96 void operator()(uint32_t i, value_type &failed_count) const {
97 const uint32_t key = Near ? i / collisions : i % (inserts / collisions);
98 if (map.insert(key, i).failed()) ++failed_count;
99 }
100 };
101
102 template <typename MapType, bool Near>
103 struct TestErase {
104 using self_type = TestErase<MapType, Near>;
105
106 using map_type = MapType;
107 using execution_space = typename MapType::execution_space;
108
109 map_type m_map;
110 uint32_t m_num_erase;
111 uint32_t m_num_duplicates;
112
TestEraseTest::Impl::TestErase113 TestErase(map_type map, uint32_t num_erases, uint32_t num_duplicates)
114 : m_map(map), m_num_erase(num_erases), m_num_duplicates(num_duplicates) {}
115
testitTest::Impl::TestErase116 void testit() {
117 execution_space().fence();
118 Kokkos::parallel_for(m_num_erase, *this);
119 execution_space().fence();
120 }
121
122 KOKKOS_INLINE_FUNCTION
operator ()Test::Impl::TestErase123 void operator()(typename execution_space::size_type i) const {
124 if (Near) {
125 m_map.erase(i / m_num_duplicates);
126 } else {
127 m_map.erase(i % (m_num_erase / m_num_duplicates));
128 }
129 }
130 };
131
132 template <typename MapType>
133 struct TestFind {
134 using map_type = MapType;
135 using execution_space = typename MapType::execution_space::execution_space;
136 using value_type = uint32_t;
137
138 map_type m_map;
139 uint32_t m_num_insert;
140 uint32_t m_num_duplicates;
141 uint32_t m_max_key;
142
TestFindTest::Impl::TestFind143 TestFind(map_type map, uint32_t num_inserts, uint32_t num_duplicates)
144 : m_map(map),
145 m_num_insert(num_inserts),
146 m_num_duplicates(num_duplicates),
147 m_max_key(((num_inserts + num_duplicates) - 1) / num_duplicates) {}
148
testitTest::Impl::TestFind149 void testit(value_type &errors) {
150 execution_space().fence();
151 Kokkos::parallel_reduce(m_map.capacity(), *this, errors);
152 execution_space().fence();
153 }
154
155 KOKKOS_INLINE_FUNCTION
initTest::Impl::TestFind156 static void init(value_type &dst) { dst = 0; }
157
158 KOKKOS_INLINE_FUNCTION
joinTest::Impl::TestFind159 static void join(volatile value_type &dst, const volatile value_type &src) {
160 dst += src;
161 }
162
163 KOKKOS_INLINE_FUNCTION
operator ()Test::Impl::TestFind164 void operator()(typename execution_space::size_type i,
165 value_type &errors) const {
166 const bool expect_to_find_i =
167 (i < typename execution_space::size_type(m_max_key));
168
169 const bool exists = m_map.exists(i);
170
171 if (expect_to_find_i && !exists) ++errors;
172 if (!expect_to_find_i && exists) ++errors;
173 }
174 };
175
176 } // namespace Impl
177
178 // MSVC reports a syntax error for this test.
179 // WORKAROUND MSVC
180 #ifndef _WIN32
181 template <typename Device>
test_insert(uint32_t num_nodes,uint32_t num_inserts,uint32_t num_duplicates,bool near)182 void test_insert(uint32_t num_nodes, uint32_t num_inserts,
183 uint32_t num_duplicates, bool near) {
184 using map_type = Kokkos::UnorderedMap<uint32_t, uint32_t, Device>;
185 using const_map_type =
186 Kokkos::UnorderedMap<const uint32_t, const uint32_t, Device>;
187
188 const uint32_t expected_inserts =
189 (num_inserts + num_duplicates - 1u) / num_duplicates;
190
191 map_type map;
192 map.rehash(num_nodes, false);
193
194 if (near) {
195 Impl::TestInsert<map_type, true> test_insert(map, num_inserts,
196 num_duplicates);
197 test_insert.testit();
198 } else {
199 Impl::TestInsert<map_type, false> test_insert(map, num_inserts,
200 num_duplicates);
201 test_insert.testit();
202 }
203
204 const bool print_list = false;
205 if (print_list) {
206 Kokkos::Impl::UnorderedMapPrint<map_type> f(map);
207 f.apply();
208 }
209
210 const uint32_t map_size = map.size();
211
212 ASSERT_FALSE(map.failed_insert());
213 {
214 EXPECT_EQ(expected_inserts, map_size);
215
216 {
217 uint32_t find_errors = 0;
218 Impl::TestFind<const_map_type> test_find(map, num_inserts,
219 num_duplicates);
220 test_find.testit(find_errors);
221 EXPECT_EQ(0u, find_errors);
222 }
223
224 map.begin_erase();
225 Impl::TestErase<map_type, false> test_erase(map, num_inserts,
226 num_duplicates);
227 test_erase.testit();
228 map.end_erase();
229 EXPECT_EQ(0u, map.size());
230 }
231 }
232 #endif
233
234 template <typename Device>
test_failed_insert(uint32_t num_nodes)235 void test_failed_insert(uint32_t num_nodes) {
236 using map_type = Kokkos::UnorderedMap<uint32_t, uint32_t, Device>;
237
238 map_type map(num_nodes);
239 Impl::TestInsert<map_type> test_insert(map, 2u * num_nodes, 1u);
240 test_insert.testit(false /*don't rehash on fail*/);
241 typename Device::execution_space().fence();
242
243 EXPECT_TRUE(map.failed_insert());
244 }
245
246 template <typename Device>
test_deep_copy(uint32_t num_nodes)247 void test_deep_copy(uint32_t num_nodes) {
248 using map_type = Kokkos::UnorderedMap<uint32_t, uint32_t, Device>;
249 using const_map_type =
250 Kokkos::UnorderedMap<const uint32_t, const uint32_t, Device>;
251
252 using host_map_type = typename map_type::HostMirror;
253
254 map_type map;
255 map.rehash(num_nodes, false);
256
257 {
258 Impl::TestInsert<map_type> test_insert(map, num_nodes, 1);
259 test_insert.testit();
260 ASSERT_EQ(map.size(), num_nodes);
261 ASSERT_FALSE(map.failed_insert());
262 {
263 uint32_t find_errors = 0;
264 Impl::TestFind<map_type> test_find(map, num_nodes, 1);
265 test_find.testit(find_errors);
266 EXPECT_EQ(find_errors, 0u);
267 }
268 }
269
270 host_map_type hmap;
271 Kokkos::deep_copy(hmap, map);
272
273 ASSERT_EQ(map.size(), hmap.size());
274 ASSERT_EQ(map.capacity(), hmap.capacity());
275 {
276 uint32_t find_errors = 0;
277 Impl::TestFind<host_map_type> test_find(hmap, num_nodes, 1);
278 test_find.testit(find_errors);
279 EXPECT_EQ(find_errors, 0u);
280 }
281
282 map_type mmap;
283 Kokkos::deep_copy(mmap, hmap);
284
285 const_map_type cmap = mmap;
286
287 EXPECT_EQ(cmap.size(), num_nodes);
288
289 {
290 uint32_t find_errors = 0;
291 Impl::TestFind<const_map_type> test_find(cmap, num_nodes, 1);
292 test_find.testit(find_errors);
293 EXPECT_EQ(find_errors, 0u);
294 }
295 }
296
297 // FIXME_SYCL wrong results on Nvidia GPUs but correct on Host and Intel GPUs
298 // FIXME_HIP
299 // WORKAROUND MSVC
300 #if !(defined(KOKKOS_ENABLE_HIP) && (HIP_VERSION < 401)) && \
301 !defined(_WIN32) && !defined(KOKKOS_ENABLE_SYCL)
TEST(TEST_CATEGORY,UnorderedMap_insert)302 TEST(TEST_CATEGORY, UnorderedMap_insert) {
303 for (int i = 0; i < 500; ++i) {
304 test_insert<TEST_EXECSPACE>(100000, 90000, 100, true);
305 test_insert<TEST_EXECSPACE>(100000, 90000, 100, false);
306 }
307 }
308 #endif
309
TEST(TEST_CATEGORY,UnorderedMap_failed_insert)310 TEST(TEST_CATEGORY, UnorderedMap_failed_insert) {
311 for (int i = 0; i < 1000; ++i) test_failed_insert<TEST_EXECSPACE>(10000);
312 }
313
TEST(TEST_CATEGORY,UnorderedMap_deep_copy)314 TEST(TEST_CATEGORY, UnorderedMap_deep_copy) {
315 for (int i = 0; i < 2; ++i) test_deep_copy<TEST_EXECSPACE>(10000);
316 }
317
TEST(TEST_CATEGORY,UnorderedMap_valid_empty)318 TEST(TEST_CATEGORY, UnorderedMap_valid_empty) {
319 using Key = int;
320 using Value = int;
321 using Map = Kokkos::UnorderedMap<Key, Value, TEST_EXECSPACE>;
322
323 Map m{};
324 Map n{};
325 n = Map{m.capacity()};
326 n.rehash(m.capacity());
327 Kokkos::deep_copy(n, m);
328 ASSERT_TRUE(m.is_allocated());
329 ASSERT_TRUE(n.is_allocated());
330 }
331
332 } // namespace Test
333
334 #endif // KOKKOS_TEST_UNORDERED_MAP_HPP
335