Open3D (C++ API)  0.19.0
Loading...
Searching...
No Matches
SYCLHashBackend.h
Go to the documentation of this file.
1// ----------------------------------------------------------------------------
2// - Open3D: www.open3d.org -
3// ----------------------------------------------------------------------------
4// Copyright (c) 2018-2024 www.open3d.org
5// SPDX-License-Identifier: MIT
6// ----------------------------------------------------------------------------
7
85
86#pragma once
87
88#include <algorithm>
89#include <cstdint>
90#include <memory>
91#include <sycl/sycl.hpp>
92#include <vector>
93
101
102namespace open3d {
103namespace core {
104
107enum HashSlotState : uint32_t {
111};
112
113namespace {
114
115constexpr int64_t kHashWgSize = 1024;
117constexpr int64_t kHashBucketCountMultiplier = 2;
118
119// Packed slot: [63:36] fingerprint (28 bits), [35:32] state, [31:0] buf_index.
120inline uint64_t PackSlot(uint32_t state, buf_index_t bi, uint32_t fingerprint) {
121 return (static_cast<uint64_t>(fingerprint) << 36) |
122 (static_cast<uint64_t>(state) << 32) | static_cast<uint64_t>(bi);
123}
124
125inline void UnpackSlot(uint64_t packed,
126 uint32_t& state,
127 buf_index_t& bi,
128 uint32_t& fingerprint) {
129 bi = static_cast<buf_index_t>(packed & 0xffffffffULL);
130 state = static_cast<uint32_t>((packed >> 32) & 0xfULL);
131 fingerprint = static_cast<uint32_t>(packed >> 36);
132}
133
134// Round up to power of two for `(home + probe) & (bucket_count - 1)` indexing.
135inline int64_t NextPowerOfTwo(int64_t n) {
136 if (n <= 0) return 1;
137 n--;
138 n |= n >> 1;
139 n |= n >> 2;
140 n |= n >> 4;
141 n |= n >> 8;
142 n |= n >> 16;
143 n |= n >> 32;
144 n++;
145 return n;
146}
147
148// MurmurHash3 fmix64 finalizer on FNV-1a output before bucket mask and
149// fingerprint extract.
150inline uint64_t HashMix(uint64_t h) {
151 h ^= h >> 33;
152 h *= 0xff51afd7ed558ccdULL;
153 h ^= h >> 33;
154 h *= 0xc4ceb9fe1a85ec53ULL;
155 h ^= h >> 33;
156 return h;
157}
158
159} // namespace
160
162template <typename Key, typename Hash, typename Eq>
164 uint64_t* slot_data = nullptr;
165 int64_t bucket_count = 0;
167 Hash hash_fn{};
168 Eq eq_fn{};
169
171 buf_index_t Find(const Key& key) const {
172 const int64_t mask = bucket_count - 1;
173 const uint64_t hash = HashMix(hash_fn(key));
174 const int64_t home = static_cast<int64_t>(hash & mask);
175 const uint32_t my_fingerprint =
176 static_cast<uint32_t>((hash >> 16) & 0xfffffffULL);
177
178 for (int64_t probe = 0; probe < bucket_count; ++probe) {
179 const int64_t idx = (home + probe) & mask;
180 uint64_t packed = slot_data[idx];
181 uint32_t s;
182 buf_index_t bi;
183 uint32_t fp;
184 UnpackSlot(packed, s, bi, fp);
185 if (s == kSlotEmpty) {
186 break;
187 }
188 if (s == kSlotOccupied && fp == my_fingerprint) {
189 const Key* slot_key =
190 static_cast<const Key*>(accessor.GetKeyPtr(bi));
191 if (eq_fn(*slot_key, key)) {
192 return bi;
193 }
194 }
195 }
196 return static_cast<buf_index_t>(-1);
197 }
198};
199
201template <typename Key, typename Hash, typename Eq>
203public:
204 SYCLHashBackend(int64_t init_capacity,
205 int64_t key_dsize,
206 const std::vector<int64_t>& value_dsizes,
207 const Device& device,
208 int64_t wg_size = kHashWgSize);
210
212 void Reserve(int64_t capacity) override {}
213
214 void Insert(const void* input_keys,
215 const std::vector<const void*>& input_values_soa,
216 buf_index_t* output_buf_indices,
217 bool* output_masks,
218 int64_t count) override;
219
220 void Find(const void* input_keys,
221 buf_index_t* output_buf_indices,
222 bool* output_masks,
223 int64_t count) override;
224
225 void Erase(const void* input_keys,
226 bool* output_masks,
227 int64_t count) override;
228
229 int64_t GetActiveIndices(buf_index_t* output_indices) override;
230
231 void Clear() override;
232
233 int64_t Size() const override;
235 int64_t GetNonEmptyCount() const override;
236 int64_t GetBucketCount() const override;
237 std::vector<int64_t> BucketSizes() const override;
238 float LoadFactor() const override;
239
240 void Allocate(int64_t capacity) override;
241 void Free() override;
242
251
252protected:
254
255 uint64_t* slot_data_ = nullptr;
256 int* occupied_count_ = nullptr;
257 int* non_empty_count_ = nullptr;
258 int64_t bucket_count_ = 0;
259 int64_t wg_size_ = kHashWgSize;
260
261 sycl::queue queue_;
262};
263
264template <typename Key, typename Hash, typename Eq>
266 int64_t init_capacity,
267 int64_t key_dsize,
268 const std::vector<int64_t>& value_dsizes,
269 const Device& device,
270 int64_t wg_size)
271 : DeviceHashBackend(init_capacity, key_dsize, value_dsizes, device),
272 wg_size_(wg_size),
273 queue_(sy::SYCLContext::GetInstance().GetDefaultQueue(device)) {
274 const int64_t device_max_wg_size = static_cast<int64_t>(
275 queue_.get_device()
276 .get_info<sycl::info::device::max_work_group_size>());
277 wg_size_ = std::min(wg_size_, std::max<int64_t>(1, device_max_wg_size));
278 Allocate(init_capacity);
279}
280
281template <typename Key, typename Hash, typename Eq>
285
286template <typename Key, typename Hash, typename Eq>
288 if (!occupied_count_) {
289 return 0;
290 }
291 int count = 0;
292 MemoryManager::MemcpyToHost(&count, occupied_count_, this->device_,
293 sizeof(int));
294 return static_cast<int64_t>(count);
295}
296
297template <typename Key, typename Hash, typename Eq>
299 if (!non_empty_count_) {
300 return 0;
301 }
302 int count = 0;
303 MemoryManager::MemcpyToHost(&count, non_empty_count_, this->device_,
304 sizeof(int));
305 return static_cast<int64_t>(count);
306}
307
308template <typename Key, typename Hash, typename Eq>
310 return bucket_count_;
311}
312
313template <typename Key, typename Hash, typename Eq>
315 utility::LogError("Unimplemented");
316}
317
318template <typename Key, typename Hash, typename Eq>
320 return float(Size()) / float(bucket_count_);
321}
322
323template <typename Key, typename Hash, typename Eq>
325 const void* input_keys,
326 const std::vector<const void*>& input_values_soa,
327 buf_index_t* output_buf_indices,
328 bool* output_masks,
329 int64_t count) {
330 if (count == 0) return;
331
332 const Key* keys = static_cast<const Key*>(input_keys);
333 const int n_values = static_cast<int>(input_values_soa.size());
334
335 if (n_values > 16) {
336 utility::LogError(
337 "SYCL hashmap supports up to 16 value arrays, but got {}.",
338 n_values);
339 }
340
341 // Copy host-side SoA pointers into a struct capturable by the SYCL kernel.
342 struct ValuesSoA {
343 const void* ptrs[16];
344 } values_soa;
345 for (int i = 0; i < n_values; ++i) {
346 values_soa.ptrs[i] = input_values_soa[i];
347 }
348
349 // Bulk-reserve `count` heap slots up front, one per thread, via plain
350 // index arithmetic (no atomics) -- mirrors CUDA's SlabHashBackend::Insert
351 // (see InsertKernelPass0). This is what makes the DeviceFree() call below
352 // safe: since this kernel never calls DeviceAllocate() itself, its
353 // DeviceFree() calls cannot race with a concurrent allocation of the same
354 // slot. Interleaving DeviceAllocate() (lazily, inside the probe loop) and
355 // DeviceFree() (on the leak path) within the same kernel invocation was
356 // tried first, but heap_top_ fetch_add/fetch_sub pairs only order the
357 // atomic counter itself, not the plain heap_[] array reads/writes tied to
358 // it, so concurrent Allocate/Free calls could still observe/overwrite
359 // each other's heap_[] slot before the intended visibility order was
360 // established, corrupting the free list (observed as extra/duplicate
361 // buf_indices ending up marked valid).
362 const int prev_heap_top = this->buffer_->GetHeapTopIndex();
363 {
364 const int new_heap_top = prev_heap_top + static_cast<int>(count);
365 queue_.memcpy(buffer_accessor_.heap_top_, &new_heap_top, sizeof(int))
366 .wait();
367 }
368
369 SYCLHashBackendBufferAccessor accessor = buffer_accessor_;
370 uint64_t* slot_data = slot_data_;
371 const int64_t bucket_count = bucket_count_;
372 const int64_t capacity = accessor.capacity_;
373 int* occupied_count = occupied_count_;
374 int* non_empty_count = non_empty_count_;
375 constexpr int kMaxOuterIter = 1 << 20; // Termination if table is full.
376 Hash hash_fn;
377 Eq eq_fn;
378
379 const int64_t common_block_size = buffer_accessor_.common_block_size_;
380
381 auto insert_kernel = [=](sycl::nd_item<1>
382 item) [[intel::kernel_args_restrict]] {
383 const int64_t tid = item.get_global_id(0);
384 int my_new_occupied = 0;
385 int my_new_nonempty = 0;
386
387 if (tid < count) {
388 sycl::atomic_fence(sycl::memory_order::seq_cst,
389 sycl::memory_scope::device);
390
391 const Key key = keys[tid];
392 output_buf_indices[tid] = 0;
393 output_masks[tid] = false;
394
395 const int64_t mask = bucket_count - 1;
396 const uint64_t hash = HashMix(hash_fn(key));
397 const int64_t home = static_cast<int64_t>(hash & mask);
398 const uint32_t my_fingerprint =
399 static_cast<uint32_t>((hash >> 16) & 0xfffffffULL);
400
401 // Slot for this thread was bulk-reserved on the host before
402 // kernel launch (see prev_heap_top above): no atomic is needed
403 // here, and no other thread can read/write this exact heap_[]
404 // entry, since every tid maps to a disjoint reserved_index.
405 const int64_t reserved_index =
406 static_cast<int64_t>(prev_heap_top) + tid;
407 buf_index_t my_bi =
408 (reserved_index < capacity)
409 ? accessor.heap_[reserved_index]
411 bool key_published = false;
413 // Publish key/values immediately: whether this slot is
414 // ultimately kept (CAS succeeds) or returned to the heap
415 // (duplicate found / table full) is decided below, but the
416 // write itself never races with anything since this slot is
417 // exclusively owned by this thread until (at most) one
418 // DeviceFree() call at the end.
419 Key* slot_key = static_cast<Key*>(accessor.GetKeyPtr(my_bi));
420 *slot_key = key;
421
422 for (int j = 0; j < n_values; ++j) {
423 const int64_t blocks =
424 accessor.value_blocks_per_element_[j];
425 DISPATCH_DIVISOR_SIZE_TO_BLOCK_T_SYCL(
426 common_block_size, [&]() {
427 using val_block_t = block_t;
428 val_block_t* dst =
429 reinterpret_cast<val_block_t*>(
430 accessor.GetValuePtr(my_bi, j));
431 const val_block_t* src =
432 reinterpret_cast<const val_block_t*>(
433 values_soa.ptrs[j]) +
434 blocks * tid;
435 for (int64_t b = 0; b < blocks; ++b) {
436 dst[b] = src[b];
437 }
438 });
439 }
440
441 sycl::atomic_fence(sycl::memory_order::seq_cst,
442 sycl::memory_scope::device);
443 key_published = true;
444 }
445
446 bool finished = false;
447 int outer_iter = 0;
448 while (!finished) {
449 if (++outer_iter > kMaxOuterIter) {
450 break;
451 }
452 int64_t first_deleted = -1;
453 bool restart = false;
454
455 for (int64_t probe = 0; probe < bucket_count; ++probe) {
456 const int64_t idx = (home + probe) & mask;
457 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
458 sycl::memory_scope::device>
459 st(slot_data[idx]);
460
461 uint64_t packed = st.load(sycl::memory_order::acquire);
462 uint32_t s;
463 buf_index_t bi;
464 uint32_t fp;
465 UnpackSlot(packed, s, bi, fp);
466
467 if (s == kSlotOccupied) {
468 if (fp == my_fingerprint) {
469 sycl::atomic_fence(sycl::memory_order::seq_cst,
470 sycl::memory_scope::device);
471 const Key* slot_key = static_cast<const Key*>(
472 accessor.GetKeyPtr(bi));
473 if (eq_fn(*slot_key, key)) {
474 output_buf_indices[tid] = bi;
475 output_masks[tid] = false;
476 finished = true;
477 break;
478 }
479 }
480 continue;
481 }
482
483 if (s == kSlotDeleted) {
484 if (first_deleted < 0) first_deleted = idx;
485 continue;
486 }
487
488 if (!key_published) {
489 // Heap was exhausted before kernel launch (no slot
490 // reserved for this thread): nothing to insert.
491 break;
492 }
493
494 const int64_t target =
495 (first_deleted >= 0) ? first_deleted : idx;
496 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
497 sycl::memory_scope::device>
498 tst(slot_data[target]);
499 uint64_t expected_packed =
500 (first_deleted >= 0)
501 ? tst.load(sycl::memory_order::acquire)
502 : 0ULL;
503 const uint32_t prev_state = static_cast<uint32_t>(
504 (expected_packed >> 32) & 0xfULL);
505 if ((prev_state == kSlotEmpty ||
506 prev_state == kSlotDeleted) &&
507 tst.compare_exchange_strong(
508 expected_packed,
509 PackSlot(kSlotOccupied, my_bi, my_fingerprint),
510 sycl::memory_order::acq_rel,
511 sycl::memory_order::relaxed)) {
512 output_buf_indices[tid] = my_bi;
513 output_masks[tid] = true;
514 my_new_occupied = 1;
515 if (prev_state == kSlotEmpty) {
516 my_new_nonempty = 1;
517 }
518 finished = true;
519 break;
520 }
521
522 restart = true;
523 break;
524 }
525
526 if (finished || !restart) {
527 break;
528 }
529 }
530
531 // This thread's reserved slot was published (key/values written)
532 // but never won the CAS that installs it into the table --
533 // either because a concurrent insert of the same key won the
534 // race first (duplicate found on a post-restart probe) or
535 // kMaxOuterIter was exhausted. Return it to the heap so capacity
536 // is not silently reduced. This DeviceFree() cannot race with a
537 // concurrent DeviceAllocate(), since slots are only ever
538 // allocated in bulk on the host before this kernel is launched.
539 if (key_published && !output_masks[tid]) {
540 accessor.DeviceFree(my_bi);
541 }
542 } // tid < count
543
544 const int wg_occupied = sycl::reduce_over_group(
545 item.get_group(), my_new_occupied, sycl::plus<int>{});
546 const int wg_nonempty = sycl::reduce_over_group(
547 item.get_group(), my_new_nonempty, sycl::plus<int>{});
548 if (item.get_local_id(0) == 0) {
549 if (wg_occupied > 0) {
550 sycl::atomic_ref<int, sycl::memory_order::relaxed,
551 sycl::memory_scope::device>
552 oc(*occupied_count);
553 oc.fetch_add(wg_occupied);
554 }
555 if (wg_nonempty > 0) {
556 sycl::atomic_ref<int, sycl::memory_order::relaxed,
557 sycl::memory_scope::device>
558 nec(*non_empty_count);
559 nec.fetch_add(wg_nonempty);
560 }
561 }
562 };
563
564 const int64_t wg_size = wg_size_;
565 const int64_t global_size = ((count + wg_size - 1) / wg_size) * wg_size;
566 queue_.submit([&](sycl::handler& cgh) {
567 cgh.parallel_for(sycl::nd_range<1>(global_size, wg_size),
568 insert_kernel);
569 }).wait_and_throw();
570}
571
572template <typename Key, typename Hash, typename Eq>
573void SYCLHashBackend<Key, Hash, Eq>::Find(const void* input_keys,
574 buf_index_t* output_buf_indices,
575 bool* output_masks,
576 int64_t count) {
577 if (count == 0) return;
578
579 const Key* keys = static_cast<const Key*>(input_keys);
580 SYCLHashBackendBufferAccessor accessor = buffer_accessor_;
581 uint64_t* slot_data = slot_data_;
582 const int64_t bucket_count = bucket_count_;
583 Hash hash_fn;
584 Eq eq_fn;
585
586 auto find_kernel =
587 [=](sycl::nd_item<1> item) [[intel::kernel_args_restrict]] {
588 const int64_t tid = item.get_global_id(0);
589 if (tid >= count) return;
590 const Key key = keys[tid];
591 const int64_t mask = bucket_count - 1;
592 const uint64_t hash = HashMix(hash_fn(key));
593 const int64_t home = static_cast<int64_t>(hash & mask);
594 const uint32_t my_fingerprint =
595 static_cast<uint32_t>((hash >> 16) & 0xfffffffULL);
596
597 bool found = false;
599 for (int64_t probe = 0; probe < bucket_count; ++probe) {
600 const int64_t idx = (home + probe) & mask;
601 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
602 sycl::memory_scope::device>
603 st(slot_data[idx]);
604 uint64_t packed = st.load(sycl::memory_order::acquire);
605 uint32_t s;
606 buf_index_t bi;
607 uint32_t fp;
608 UnpackSlot(packed, s, bi, fp);
609
610 if (s == kSlotEmpty) {
611 break;
612 }
613 if (s == kSlotOccupied && fp == my_fingerprint) {
614 const Key* slot_key =
615 static_cast<const Key*>(accessor.GetKeyPtr(bi));
616 if (eq_fn(*slot_key, key)) {
617 found = true;
618 result = bi;
619 break;
620 }
621 }
622 }
623 output_masks[tid] = found;
624 output_buf_indices[tid] = found ? result : 0;
625 };
626
627 const int64_t wg_size = wg_size_;
628 const int64_t global_size = ((count + wg_size - 1) / wg_size) * wg_size;
629 queue_.submit([&](sycl::handler& cgh) {
630 cgh.parallel_for(sycl::nd_range<1>(global_size, wg_size),
631 find_kernel);
632 }).wait_and_throw();
633}
634
635template <typename Key, typename Hash, typename Eq>
636void SYCLHashBackend<Key, Hash, Eq>::Erase(const void* input_keys,
637 bool* output_masks,
638 int64_t count) {
639 if (count == 0) return;
640
641 const Key* keys = static_cast<const Key*>(input_keys);
642 SYCLHashBackendBufferAccessor accessor = buffer_accessor_;
643 uint64_t* slot_data = slot_data_;
644 const int64_t bucket_count = bucket_count_;
645 int* occupied_count = occupied_count_;
646 Hash hash_fn;
647 Eq eq_fn;
648
649 auto erase_kernel = [=](sycl::nd_item<1>
650 item) [[intel::kernel_args_restrict]] {
651 const int64_t tid = item.get_global_id(0);
652 if (tid >= count) return;
653 const Key key = keys[tid];
654 const int64_t mask = bucket_count - 1;
655 const uint64_t hash = HashMix(hash_fn(key));
656 const int64_t home = static_cast<int64_t>(hash & mask);
657 const uint32_t my_fingerprint =
658 static_cast<uint32_t>((hash >> 16) & 0xfffffffULL);
659
660 bool erased = false;
661 for (int64_t probe = 0; probe < bucket_count; ++probe) {
662 const int64_t idx = (home + probe) & mask;
663 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
664 sycl::memory_scope::device>
665 st(slot_data[idx]);
666 uint64_t packed = st.load(sycl::memory_order::acquire);
667 uint32_t s;
668 buf_index_t bi;
669 uint32_t fp;
670 UnpackSlot(packed, s, bi, fp);
671
672 if (s == kSlotEmpty) {
673 break;
674 }
675 if (s == kSlotOccupied && fp == my_fingerprint) {
676 const Key* slot_key =
677 static_cast<const Key*>(accessor.GetKeyPtr(bi));
678 if (eq_fn(*slot_key, key)) {
679 uint64_t expected_packed = packed;
680 uint64_t deleted_val = PackSlot(kSlotDeleted, bi, fp);
681 if (st.compare_exchange_strong(
682 expected_packed, deleted_val,
683 sycl::memory_order::acq_rel,
684 sycl::memory_order::relaxed)) {
685 accessor.DeviceFree(bi);
686 erased = true;
687 sycl::atomic_ref<int, sycl::memory_order::relaxed,
688 sycl::memory_scope::device>
689 oc(*occupied_count);
690 oc.fetch_sub(1);
691 }
692 break;
693 }
694 }
695 }
696 output_masks[tid] = erased;
697 };
698
699 const int64_t wg_size = wg_size_;
700 const int64_t global_size = ((count + wg_size - 1) / wg_size) * wg_size;
701 queue_.submit([&](sycl::handler& cgh) {
702 cgh.parallel_for(sycl::nd_range<1>(global_size, wg_size),
703 erase_kernel);
704 }).wait_and_throw();
705}
706
707template <typename Key, typename Hash, typename Eq>
709 buf_index_t* output_indices) {
710 uint64_t* slot_data = slot_data_;
711 const int64_t bucket_count = bucket_count_;
712
713 int* d_count = static_cast<int*>(
714 MemoryManager::Malloc(sizeof(int), this->device_));
715 queue_.memset(d_count, 0, sizeof(int)).wait_and_throw();
716
717 const int64_t kWgSize = wg_size_;
718
719 auto scan_kernel = [=](sycl::nd_item<1> item) {
720 int64_t idx = item.get_global_id(0);
721 auto group = item.get_group();
722
723 bool is_occupied = false;
724 buf_index_t bi = 0;
725 if (idx < bucket_count) {
726 const uint64_t packed = slot_data[idx];
727 uint32_t s = static_cast<uint32_t>((packed >> 32) & 0xfULL);
728 if (s == kSlotOccupied) {
729 is_occupied = true;
730 bi = static_cast<buf_index_t>(packed & 0xffffffffULL);
731 }
732 }
733
734 int local_val = is_occupied ? 1 : 0;
735 int local_offset = sycl::exclusive_scan_over_group(group, local_val,
736 sycl::plus<int>{});
737 int group_total =
738 sycl::reduce_over_group(group, local_val, sycl::plus<int>{});
739
740 int group_start = 0;
741 if (item.get_local_id(0) == 0 && group_total > 0) {
742 sycl::atomic_ref<int, sycl::memory_order::relaxed,
743 sycl::memory_scope::device>
744 counter(*d_count);
745 group_start = counter.fetch_add(group_total);
746 }
747 group_start = sycl::group_broadcast(group, group_start, 0);
748
749 if (is_occupied) {
750 output_indices[group_start + local_offset] = bi;
751 }
752 };
753
754 int64_t global_size = ((bucket_count + kWgSize - 1) / kWgSize) * kWgSize;
755 queue_.submit([&](sycl::handler& cgh) {
756 cgh.parallel_for(sycl::nd_range<1>(global_size, kWgSize),
757 scan_kernel);
758 }).wait_and_throw();
759
760 int count = 0;
761 MemoryManager::MemcpyToHost(&count, d_count, this->device_, sizeof(int));
762 MemoryManager::Free(d_count, this->device_);
763 return static_cast<int64_t>(count);
764}
765
766template <typename Key, typename Hash, typename Eq>
768 this->buffer_->ResetHeap();
769 queue_.memset(slot_data_, 0, bucket_count_ * sizeof(uint64_t))
770 .wait_and_throw();
771 if (occupied_count_) {
772 queue_.memset(occupied_count_, 0, sizeof(int)).wait_and_throw();
773 }
774 if (non_empty_count_) {
775 queue_.memset(non_empty_count_, 0, sizeof(int)).wait_and_throw();
776 }
777}
778
779template <typename Key, typename Hash, typename Eq>
781 this->capacity_ = capacity;
782 bucket_count_ = NextPowerOfTwo(
783 std::max<int64_t>(capacity * kHashBucketCountMultiplier, 1));
784
785 this->buffer_ = std::make_shared<HashBackendBuffer>(
786 this->capacity_, this->key_dsize_, this->value_dsizes_,
787 this->device_);
788 buffer_accessor_.Setup(*this->buffer_);
789
790 slot_data_ = static_cast<uint64_t*>(MemoryManager::Malloc(
791 bucket_count_ * sizeof(uint64_t), this->device_));
792 queue_.memset(slot_data_, 0, bucket_count_ * sizeof(uint64_t))
793 .wait_and_throw();
794
795 occupied_count_ = static_cast<int*>(
796 MemoryManager::Malloc(sizeof(int), this->device_));
797 queue_.memset(occupied_count_, 0, sizeof(int)).wait_and_throw();
798
799 non_empty_count_ = static_cast<int*>(
800 MemoryManager::Malloc(sizeof(int), this->device_));
801 queue_.memset(non_empty_count_, 0, sizeof(int)).wait_and_throw();
802}
803
804template <typename Key, typename Hash, typename Eq>
806 buffer_accessor_.Shutdown(this->device_);
807 if (slot_data_) {
808 MemoryManager::Free(slot_data_, this->device_);
809 slot_data_ = nullptr;
810 }
811 if (occupied_count_) {
812 MemoryManager::Free(occupied_count_, this->device_);
813 occupied_count_ = nullptr;
814 }
815 if (non_empty_count_) {
816 MemoryManager::Free(non_empty_count_, this->device_);
817 non_empty_count_ = nullptr;
818 }
819}
820
821} // namespace core
822} // namespace open3d
BitmapEventQueue * queue_
Definition BitmapWindowSystem.cpp:53
Vectorized trivial-object copy block sizes (CUDA and SYCL).
SYCL device properties and (when built) queue manager.
Device-side accessor for SYCL hash-map key/value buffers.
Real target
Definition SurfaceReconstructionPoisson.cpp:267
core::Tensor result
Definition VtkUtils.cpp:76
Definition DeviceHashBackend.h:20
Definition Device.h:18
static void MemcpyToHost(void *host_ptr, const void *src_ptr, const Device &src_device, size_t num_bytes)
Same as Memcpy, but with host (CPU:0) as default dst_device.
Definition MemoryManager.cpp:85
static void * Malloc(size_t byte_size, const Device &device)
Definition MemoryManager.cpp:22
static void Free(void *ptr, const Device &device)
Frees previously allocated memory at address ptr on device device.
Definition MemoryManager.cpp:28
Definition SYCLHashBackendBufferAccessor.h:33
void * GetKeyPtr(buf_index_t buf_index) const
Device: USM pointer to the key at buf_index.
Definition SYCLHashBackendBufferAccessor.h:135
void * GetValuePtr(buf_index_t buf_index, int value_idx=0) const
Device: USM pointer to value value_idx at buf_index.
Definition SYCLHashBackendBufferAccessor.h:139
int64_t * value_blocks_per_element_
Blocks per value for vector copy.
Definition SYCLHashBackendBufferAccessor.h:156
static constexpr buf_index_t kInvalidBufIndex
Definition SYCLHashBackendBufferAccessor.h:35
int64_t capacity_
Definition SYCLHashBackendBufferAccessor.h:158
void DeviceFree(buf_index_t buf_index) const
Definition SYCLHashBackendBufferAccessor.h:124
buf_index_t * heap_
Free-slot index stack, length capacity_.
Definition SYCLHashBackendBufferAccessor.h:144
DeviceHashBackend for SYCL devices (algorithm in file header).
Definition SYCLHashBackend.h:202
SYCLHashDeviceLookup< Key, Hash, Eq > GetDeviceLookup() const
Snapshot for device kernels; table must not be mutated while in use.
Definition SYCLHashBackend.h:244
SYCLHashBackend(int64_t init_capacity, int64_t key_dsize, const std::vector< int64_t > &value_dsizes, const Device &device, int64_t wg_size=kHashWgSize)
Definition SYCLHashBackend.h:265
~SYCLHashBackend()
Definition SYCLHashBackend.h:282
void Find(const void *input_keys, buf_index_t *output_buf_indices, bool *output_masks, int64_t count) override
Parallel find a contiguous array of keys.
Definition SYCLHashBackend.h:573
void Erase(const void *input_keys, bool *output_masks, int64_t count) override
Parallel erase a contiguous array of keys.
Definition SYCLHashBackend.h:636
int64_t GetNonEmptyCount() const override
Occupied + deleted slots (rehash guard; see file header).
Definition SYCLHashBackend.h:298
float LoadFactor() const override
Get the current load factor, defined as size / bucket count.
Definition SYCLHashBackend.h:319
void Clear() override
Clear stored map without reallocating memory.
Definition SYCLHashBackend.h:767
int64_t wg_size_
SYCL work-group size for kernels.
Definition SYCLHashBackend.h:259
std::vector< int64_t > BucketSizes() const override
Get the number of entries per bucket.
Definition SYCLHashBackend.h:314
void Free() override
Definition SYCLHashBackend.h:805
sycl::queue queue_
Definition SYCLHashBackend.h:261
void Reserve(int64_t capacity) override
No-op; use HashMap::Reserve for capacity growth.
Definition SYCLHashBackend.h:212
int * occupied_count_
Device live entry count.
Definition SYCLHashBackend.h:256
int64_t bucket_count_
Definition SYCLHashBackend.h:258
void Insert(const void *input_keys, const std::vector< const void * > &input_values_soa, buf_index_t *output_buf_indices, bool *output_masks, int64_t count) override
Parallel insert contiguous arrays of keys and values.
Definition SYCLHashBackend.h:324
SYCLHashBackendBufferAccessor buffer_accessor_
Definition SYCLHashBackend.h:253
int64_t GetActiveIndices(buf_index_t *output_indices) override
Parallel collect all iterators in the hash table.
Definition SYCLHashBackend.h:708
int64_t Size() const override
Get the size (number of valid entries) of the hash map.
Definition SYCLHashBackend.h:287
int64_t GetBucketCount() const override
Get the number of buckets of the hash map.
Definition SYCLHashBackend.h:309
int * non_empty_count_
Device occupied + tombstone count.
Definition SYCLHashBackend.h:257
void Allocate(int64_t capacity) override
Definition SYCLHashBackend.h:780
uint64_t * slot_data_
USM packed slots.
Definition SYCLHashBackend.h:255
int count
Definition FilePCD.cpp:43
uint32_t buf_index_t
Definition HashBackendBuffer.h:49
HashSlotState
Definition SYCLHashBackend.h:107
@ kSlotOccupied
Definition SYCLHashBackend.h:109
@ kSlotDeleted
Definition SYCLHashBackend.h:110
@ kSlotEmpty
Definition SYCLHashBackend.h:108
const char const char value recording_handle imu_sample recording_handle uint8_t size_t data_size k4a_record_configuration_t config target_format k4a_capture_t capture_handle k4a_imu_sample_t imu_sample uint64_t
Definition K4aPlugin.cpp:343
Definition PinholeCameraIntrinsic.cpp:16
Read-only table view for device kernels (see file header).
Definition SYCLHashBackend.h:163
uint64_t * slot_data
USM packed slot array.
Definition SYCLHashBackend.h:164
Hash hash_fn
Key hash functor.
Definition SYCLHashBackend.h:167
Eq eq_fn
Key equality functor.
Definition SYCLHashBackend.h:168
int64_t bucket_count
Power-of-two bucket count.
Definition SYCLHashBackend.h:165
SYCLHashBackendBufferAccessor accessor
Key/value buffer accessor.
Definition SYCLHashBackend.h:166
buf_index_t Find(const Key &key) const
Linear-probe lookup; returns buffer index or -1 if not found.
Definition SYCLHashBackend.h:171