Open3D (C++ API)  0.20.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-2026 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::GetQueue(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 one heap slot per thread, mirroring CUDA's
350 // SlabHashBackend::Insert (see InsertKernelPass0). Stage the indices in
351 // the output buffer before advancing heap_top_: losing threads may then
352 // call DeviceFree() without overwriting heap entries that another thread
353 // in this kernel has yet to read.
354 const int prev_heap_top = this->buffer_->GetHeapTopIndex();
355 const int64_t capacity = buffer_accessor_.capacity_;
356 if (count > capacity - prev_heap_top) {
357 utility::LogError(
358 "SYCL hashmap insertion requires {} free buffer slots, but "
359 "only {} are available.",
360 count, capacity - prev_heap_top);
361 }
362 {
363 queue_.memcpy(output_buf_indices,
364 buffer_accessor_.heap_ + prev_heap_top,
365 count * sizeof(buf_index_t))
366 .wait_and_throw();
367 const int new_heap_top = prev_heap_top + static_cast<int>(count);
368 queue_.memcpy(buffer_accessor_.heap_top_, &new_heap_top, sizeof(int))
369 .wait_and_throw();
370 }
371
372 SYCLHashBackendBufferAccessor accessor = buffer_accessor_;
373 uint64_t* slot_data = slot_data_;
374 const int64_t bucket_count = bucket_count_;
375 int* occupied_count = occupied_count_;
376 int* non_empty_count = non_empty_count_;
377 constexpr int kMaxOuterIter = 1 << 20; // Termination if table is full.
378 Hash hash_fn;
379 Eq eq_fn;
380
381 const int64_t common_block_size = buffer_accessor_.common_block_size_;
382
383 auto insert_kernel = [=](sycl::nd_item<1>
384 item) [[intel::kernel_args_restrict]] {
385 const int64_t tid = item.get_global_id(0);
386 int my_new_occupied = 0;
387 int my_new_nonempty = 0;
388
389 if (tid < count) {
390 sycl::atomic_fence(sycl::memory_order::seq_cst,
391 sycl::memory_scope::device);
392
393 const buf_index_t my_bi = output_buf_indices[tid];
394 const Key key = keys[tid];
395 output_buf_indices[tid] = 0;
396 output_masks[tid] = false;
397
398 const int64_t mask = bucket_count - 1;
399 const uint64_t hash = HashMix(hash_fn(key));
400 const int64_t home = static_cast<int64_t>(hash & mask);
401 const uint32_t my_fingerprint =
402 static_cast<uint32_t>((hash >> 16) & 0xfffffffULL);
403
404 bool key_published = false;
406 // Publish key/values immediately: whether this slot is
407 // ultimately kept (CAS succeeds) or returned to the heap
408 // (duplicate found / table full) is decided below, but the
409 // write itself never races with anything since this slot is
410 // exclusively owned by this thread until (at most) one
411 // DeviceFree() call at the end.
412 Key* slot_key = static_cast<Key*>(accessor.GetKeyPtr(my_bi));
413 *slot_key = key;
414
415 for (int j = 0; j < n_values; ++j) {
416 const int64_t blocks =
417 accessor.value_blocks_per_element_[j];
418 DISPATCH_DIVISOR_SIZE_TO_BLOCK_T_SYCL(
419 common_block_size, [&]() {
420 using val_block_t = block_t;
421 val_block_t* dst =
422 reinterpret_cast<val_block_t*>(
423 accessor.GetValuePtr(my_bi, j));
424 const val_block_t* src =
425 reinterpret_cast<const val_block_t*>(
426 values_soa.ptrs[j]) +
427 blocks * tid;
428 for (int64_t b = 0; b < blocks; ++b) {
429 dst[b] = src[b];
430 }
431 });
432 }
433
434 sycl::atomic_fence(sycl::memory_order::seq_cst,
435 sycl::memory_scope::device);
436 key_published = true;
437 }
438
439 bool finished = false;
440 int outer_iter = 0;
441 while (!finished) {
442 if (++outer_iter > kMaxOuterIter) {
443 break;
444 }
445 int64_t first_deleted = -1;
446 bool restart = false;
447
448 for (int64_t probe = 0; probe < bucket_count; ++probe) {
449 const int64_t idx = (home + probe) & mask;
450 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
451 sycl::memory_scope::device>
452 st(slot_data[idx]);
453
454 uint64_t packed = st.load(sycl::memory_order::acquire);
455 uint32_t s;
456 buf_index_t bi;
457 uint32_t fp;
458 UnpackSlot(packed, s, bi, fp);
459
460 if (s == kSlotOccupied) {
461 if (fp == my_fingerprint) {
462 sycl::atomic_fence(sycl::memory_order::seq_cst,
463 sycl::memory_scope::device);
464 const Key* slot_key = static_cast<const Key*>(
465 accessor.GetKeyPtr(bi));
466 if (eq_fn(*slot_key, key)) {
467 output_buf_indices[tid] = bi;
468 output_masks[tid] = false;
469 finished = true;
470 break;
471 }
472 }
473 continue;
474 }
475
476 if (s == kSlotDeleted) {
477 if (first_deleted < 0) first_deleted = idx;
478 continue;
479 }
480
481 if (!key_published) {
482 // Heap was exhausted before kernel launch (no slot
483 // reserved for this thread): nothing to insert.
484 break;
485 }
486
487 const int64_t target =
488 (first_deleted >= 0) ? first_deleted : idx;
489 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
490 sycl::memory_scope::device>
491 tst(slot_data[target]);
492 uint64_t expected_packed =
493 (first_deleted >= 0)
494 ? tst.load(sycl::memory_order::acquire)
495 : 0ULL;
496 const uint32_t prev_state = static_cast<uint32_t>(
497 (expected_packed >> 32) & 0xfULL);
498 if ((prev_state == kSlotEmpty ||
499 prev_state == kSlotDeleted) &&
500 tst.compare_exchange_strong(
501 expected_packed,
502 PackSlot(kSlotOccupied, my_bi, my_fingerprint),
503 sycl::memory_order::acq_rel,
504 sycl::memory_order::relaxed)) {
505 output_buf_indices[tid] = my_bi;
506 output_masks[tid] = true;
507 my_new_occupied = 1;
508 if (prev_state == kSlotEmpty) {
509 my_new_nonempty = 1;
510 }
511 finished = true;
512 break;
513 }
514
515 restart = true;
516 break;
517 }
518
519 if (finished || !restart) {
520 break;
521 }
522 }
523
524 // Return a published-but-unowned reservation after losing a
525 // duplicate-key CAS race or exhausting kMaxOuterIter. All
526 // reservations were staged before launch, so this heap write
527 // cannot overwrite another thread's pending reservation read.
528 if (key_published && !output_masks[tid]) {
529 accessor.DeviceFree(my_bi);
530 }
531 } // tid < count
532
533 const int wg_occupied = sycl::reduce_over_group(
534 item.get_group(), my_new_occupied, sycl::plus<int>{});
535 const int wg_nonempty = sycl::reduce_over_group(
536 item.get_group(), my_new_nonempty, sycl::plus<int>{});
537 if (item.get_local_id(0) == 0) {
538 if (wg_occupied > 0) {
539 sycl::atomic_ref<int, sycl::memory_order::relaxed,
540 sycl::memory_scope::device>
541 oc(*occupied_count);
542 oc.fetch_add(wg_occupied);
543 }
544 if (wg_nonempty > 0) {
545 sycl::atomic_ref<int, sycl::memory_order::relaxed,
546 sycl::memory_scope::device>
547 nec(*non_empty_count);
548 nec.fetch_add(wg_nonempty);
549 }
550 }
551 };
552
553 const int64_t wg_size = wg_size_;
554 const int64_t global_size = ((count + wg_size - 1) / wg_size) * wg_size;
555 queue_.submit([&](sycl::handler& cgh) {
556 cgh.parallel_for(sycl::nd_range<1>(global_size, wg_size),
557 insert_kernel);
558 }).wait_and_throw();
559}
560
561template <typename Key, typename Hash, typename Eq>
562void SYCLHashBackend<Key, Hash, Eq>::Find(const void* input_keys,
563 buf_index_t* output_buf_indices,
564 bool* output_masks,
565 int64_t count) {
566 if (count == 0) return;
567
568 const Key* keys = static_cast<const Key*>(input_keys);
569 SYCLHashBackendBufferAccessor accessor = buffer_accessor_;
570 uint64_t* slot_data = slot_data_;
571 const int64_t bucket_count = bucket_count_;
572 Hash hash_fn;
573 Eq eq_fn;
574
575 auto find_kernel =
576 [=](sycl::nd_item<1> item) [[intel::kernel_args_restrict]] {
577 const int64_t tid = item.get_global_id(0);
578 if (tid >= count) return;
579 const Key key = keys[tid];
580 const int64_t mask = bucket_count - 1;
581 const uint64_t hash = HashMix(hash_fn(key));
582 const int64_t home = static_cast<int64_t>(hash & mask);
583 const uint32_t my_fingerprint =
584 static_cast<uint32_t>((hash >> 16) & 0xfffffffULL);
585
586 bool found = false;
588 for (int64_t probe = 0; probe < bucket_count; ++probe) {
589 const int64_t idx = (home + probe) & mask;
590 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
591 sycl::memory_scope::device>
592 st(slot_data[idx]);
593 uint64_t packed = st.load(sycl::memory_order::acquire);
594 uint32_t s;
595 buf_index_t bi;
596 uint32_t fp;
597 UnpackSlot(packed, s, bi, fp);
598
599 if (s == kSlotEmpty) {
600 break;
601 }
602 if (s == kSlotOccupied && fp == my_fingerprint) {
603 const Key* slot_key =
604 static_cast<const Key*>(accessor.GetKeyPtr(bi));
605 if (eq_fn(*slot_key, key)) {
606 found = true;
607 result = bi;
608 break;
609 }
610 }
611 }
612 output_masks[tid] = found;
613 output_buf_indices[tid] = found ? result : 0;
614 };
615
616 const int64_t wg_size = wg_size_;
617 const int64_t global_size = ((count + wg_size - 1) / wg_size) * wg_size;
618 queue_.submit([&](sycl::handler& cgh) {
619 cgh.parallel_for(sycl::nd_range<1>(global_size, wg_size),
620 find_kernel);
621 }).wait_and_throw();
622}
623
624template <typename Key, typename Hash, typename Eq>
625void SYCLHashBackend<Key, Hash, Eq>::Erase(const void* input_keys,
626 bool* output_masks,
627 int64_t count) {
628 if (count == 0) return;
629
630 const Key* keys = static_cast<const Key*>(input_keys);
631 SYCLHashBackendBufferAccessor accessor = buffer_accessor_;
632 uint64_t* slot_data = slot_data_;
633 const int64_t bucket_count = bucket_count_;
634 int* occupied_count = occupied_count_;
635 Hash hash_fn;
636 Eq eq_fn;
637
638 auto erase_kernel = [=](sycl::nd_item<1>
639 item) [[intel::kernel_args_restrict]] {
640 const int64_t tid = item.get_global_id(0);
641 if (tid >= count) return;
642 const Key key = keys[tid];
643 const int64_t mask = bucket_count - 1;
644 const uint64_t hash = HashMix(hash_fn(key));
645 const int64_t home = static_cast<int64_t>(hash & mask);
646 const uint32_t my_fingerprint =
647 static_cast<uint32_t>((hash >> 16) & 0xfffffffULL);
648
649 bool erased = false;
650 for (int64_t probe = 0; probe < bucket_count; ++probe) {
651 const int64_t idx = (home + probe) & mask;
652 sycl::atomic_ref<uint64_t, sycl::memory_order::relaxed,
653 sycl::memory_scope::device>
654 st(slot_data[idx]);
655 uint64_t packed = st.load(sycl::memory_order::acquire);
656 uint32_t s;
657 buf_index_t bi;
658 uint32_t fp;
659 UnpackSlot(packed, s, bi, fp);
660
661 if (s == kSlotEmpty) {
662 break;
663 }
664 if (s == kSlotOccupied && fp == my_fingerprint) {
665 const Key* slot_key =
666 static_cast<const Key*>(accessor.GetKeyPtr(bi));
667 if (eq_fn(*slot_key, key)) {
668 uint64_t expected_packed = packed;
669 uint64_t deleted_val = PackSlot(kSlotDeleted, bi, fp);
670 if (st.compare_exchange_strong(
671 expected_packed, deleted_val,
672 sycl::memory_order::acq_rel,
673 sycl::memory_order::relaxed)) {
674 accessor.DeviceFree(bi);
675 erased = true;
676 sycl::atomic_ref<int, sycl::memory_order::relaxed,
677 sycl::memory_scope::device>
678 oc(*occupied_count);
679 oc.fetch_sub(1);
680 }
681 break;
682 }
683 }
684 }
685 output_masks[tid] = erased;
686 };
687
688 const int64_t wg_size = wg_size_;
689 const int64_t global_size = ((count + wg_size - 1) / wg_size) * wg_size;
690 queue_.submit([&](sycl::handler& cgh) {
691 cgh.parallel_for(sycl::nd_range<1>(global_size, wg_size),
692 erase_kernel);
693 }).wait_and_throw();
694}
695
696template <typename Key, typename Hash, typename Eq>
698 buf_index_t* output_indices) {
699 uint64_t* slot_data = slot_data_;
700 const int64_t bucket_count = bucket_count_;
701
702 int* d_count = static_cast<int*>(
703 MemoryManager::Malloc(sizeof(int), this->device_));
704 // scan_kernel below touches d_count via raw USM, so on an out-of-order
705 // queue the SYCL runtime cannot infer the ordering on its own -- an
706 // explicit depends_on() is required for correctness, but nothing needs the
707 // zeroed value host-side, so a device-side dependency suffices.
708 sycl::event memset_event = queue_.memset(d_count, 0, sizeof(int));
709
710 const int64_t kWgSize = wg_size_;
711
712 auto scan_kernel = [=](sycl::nd_item<1> item) {
713 int64_t idx = item.get_global_id(0);
714 auto group = item.get_group();
715
716 bool is_occupied = false;
717 buf_index_t bi = 0;
718 if (idx < bucket_count) {
719 const uint64_t packed = slot_data[idx];
720 uint32_t s = static_cast<uint32_t>((packed >> 32) & 0xfULL);
721 if (s == kSlotOccupied) {
722 is_occupied = true;
723 bi = static_cast<buf_index_t>(packed & 0xffffffffULL);
724 }
725 }
726
727 int local_val = is_occupied ? 1 : 0;
728 int local_offset = sycl::exclusive_scan_over_group(group, local_val,
729 sycl::plus<int>{});
730 int group_total =
731 sycl::reduce_over_group(group, local_val, sycl::plus<int>{});
732
733 int group_start = 0;
734 if (item.get_local_id(0) == 0 && group_total > 0) {
735 sycl::atomic_ref<int, sycl::memory_order::relaxed,
736 sycl::memory_scope::device>
737 counter(*d_count);
738 group_start = counter.fetch_add(group_total);
739 }
740 group_start = sycl::group_broadcast(group, group_start, 0);
741
742 if (is_occupied) {
743 output_indices[group_start + local_offset] = bi;
744 }
745 };
746
747 int64_t global_size = ((bucket_count + kWgSize - 1) / kWgSize) * kWgSize;
748 queue_.submit([&](sycl::handler& cgh) {
749 cgh.depends_on(memset_event);
750 cgh.parallel_for(sycl::nd_range<1>(global_size, kWgSize),
751 scan_kernel);
752 }).wait_and_throw();
753
754 int count = 0;
755 MemoryManager::MemcpyToHost(&count, d_count, this->device_, sizeof(int));
756 MemoryManager::Free(d_count, this->device_);
757 return static_cast<int64_t>(count);
758}
759
760template <typename Key, typename Hash, typename Eq>
762 this->buffer_->ResetHeap();
763 // These three memsets touch disjoint USM allocations, so their relative
764 // order doesn't matter for correctness; queue::wait() blocks on *all*
765 // command groups previously submitted to the queue.
766 queue_.memset(slot_data_, 0, bucket_count_ * sizeof(uint64_t));
767 if (occupied_count_) {
768 queue_.memset(occupied_count_, 0, sizeof(int));
769 }
770 if (non_empty_count_) {
771 queue_.memset(non_empty_count_, 0, sizeof(int));
772 }
773 queue_.wait_and_throw();
774}
775
776template <typename Key, typename Hash, typename Eq>
778 this->capacity_ = capacity;
779 bucket_count_ = NextPowerOfTwo(
780 std::max<int64_t>(capacity * kHashBucketCountMultiplier, 1));
781
782 this->buffer_ = std::make_shared<HashBackendBuffer>(
783 this->capacity_, this->key_dsize_, this->value_dsizes_,
784 this->device_);
785 buffer_accessor_.Setup(*this->buffer_);
786
787 slot_data_ = static_cast<uint64_t*>(MemoryManager::Malloc(
788 bucket_count_ * sizeof(uint64_t), this->device_));
789 queue_.memset(slot_data_, 0, bucket_count_ * sizeof(uint64_t));
790
791 occupied_count_ = static_cast<int*>(
792 MemoryManager::Malloc(sizeof(int), this->device_));
793 queue_.memset(occupied_count_, 0, sizeof(int));
794
795 non_empty_count_ = static_cast<int*>(
796 MemoryManager::Malloc(sizeof(int), this->device_));
797 // As in Clear() above: these three memsets are on disjoint USM
798 // allocations, so there is a single wait at the end (blocking on all
799 // command groups previously submitted to the queue).
800 queue_.memset(non_empty_count_, 0, sizeof(int)).wait_and_throw();
801}
802
803template <typename Key, typename Hash, typename Eq>
805 buffer_accessor_.Shutdown(this->device_);
806 if (slot_data_) {
807 MemoryManager::Free(slot_data_, this->device_);
808 slot_data_ = nullptr;
809 }
810 if (occupied_count_) {
811 MemoryManager::Free(occupied_count_, this->device_);
812 occupied_count_ = nullptr;
813 }
814 if (non_empty_count_) {
815 MemoryManager::Free(non_empty_count_, this->device_);
816 non_empty_count_ = nullptr;
817 }
818}
819
820} // namespace core
821} // 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:270
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:133
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:137
int64_t * value_blocks_per_element_
Blocks per value for vector copy.
Definition SYCLHashBackendBufferAccessor.h:154
static constexpr buf_index_t kInvalidBufIndex
Definition SYCLHashBackendBufferAccessor.h:35
void DeviceFree(buf_index_t buf_index) const
Definition SYCLHashBackendBufferAccessor.h:122
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:562
void Erase(const void *input_keys, bool *output_masks, int64_t count) override
Parallel erase a contiguous array of keys.
Definition SYCLHashBackend.h:625
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:761
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:804
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:697
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:777
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