Exhaustive tiles are copied as they are, since every slot passes in order. Threshold tiles are packed on the device: a scan of the pass-bit words gives each word's first packed position, and a gather writes the passing records straight into the host buffer.
427 {
430 host.layout_.word_offsets =
nullptr;
435 host.reserve(
host.results_, slots, stream);
436 if (slots != 0U) {
438 cuda::copy_bytes(
439 stream,
441 cuda::std::span{
host.results_.data(), slots}
442 )
443 );
444 }
445 if (
host.has_match_counts_) {
446 host.reserve(
host.match_counts_, slots, stream);
447 if (slots != 0U) {
449 cuda::copy_bytes(
450 stream,
452 cuda::std::span{
host.match_counts_.data(), slots}
453 )
454 );
455 }
456 }
459 }
460
462 host.reserve(
host.pass_bits_, words, stream);
463 host.reserve(
host.word_offsets_, words, stream);
464 if (words == 0U) {
468 }
469 auto const popcounts = cuda::make_transform_iterator(
tile.
pass_bits, detail::word_popcount{});
470 size_t scan_bytes = 0;
472 cub::DeviceScan::ExclusiveSum(
473 nullptr,
474 scan_bytes,
475 popcounts,
476 host.word_offsets_.data(),
477 static_cast<int64_t>(words),
478 stream.get()
479 )
480 );
482 cuda::make_device_buffer<uint8_t>(stream, stream.device(), scan_bytes, cuda::no_init)
483 );
485 cub::DeviceScan::ExclusiveSum(
486 scan_storage.data(),
487 scan_bytes,
488 popcounts,
489 host.word_offsets_.data(),
490 static_cast<int64_t>(words),
491 stream.get()
492 )
493 );
495 cuda::copy_bytes(
496 stream,
498 cuda::std::span{
host.pass_bits_.data(), words}
499 )
500 );
502 host.count_ =
static_cast<uint64_t
>(
host.word_offsets_.data()[
words - 1U]) +
503 static_cast<uint64_t
>(cuda::std::popcount(
host.pass_bits_.data()[words - 1U]));
504 auto const count =
static_cast<size_t>(
host.count_);
505 host.reserve(
host.results_, count, stream);
506 if (
host.has_match_counts_)
host.reserve(
host.match_counts_, count, stream);
507 if (count == 0U) {
510 }
511
512
513 constexpr size_t staging_records = size_t{1} << 23U;
514 auto const staged = std::min(count, staging_records);
516 cuda::make_device_buffer<packed_pairwise_counts>(
517 stream, stream.device(), staged, cuda::no_init
518 )
519 );
521 cuda::make_device_buffer<uint32_t>(
523 )
524 );
526 ? detail::gather_passing_kernel<true, packed_pairwise_counts>
527 : detail::gather_passing_kernel<false, packed_pairwise_counts>;
531
532 auto const last_word = static_cast<size_t>(
533 std::upper_bound(offsets + first_word + 1U, offsets + words, base + staged - 32U) -
535 );
536 auto const end = last_word ==
words ?
count :
static_cast<size_t>(
offsets[last_word]);
537 kernel<<<
540 0,
541 stream.get()>>>(
545 host.word_offsets_.data(),
549 last_word,
550 base,
551 staging.data(),
553 );
555 auto const records = end - base;
556 if (records != 0U) {
558 cuda::copy_bytes(
559 stream,
560 cuda::std::span{staging.data(), records},
561 cuda::std::span{
host.results_.data() + base, records}
562 )
563 );
566 cuda::copy_bytes(
567 stream,
568 cuda::std::span{staging_matches.data(), records},
569 cuda::std::span{
host.match_counts_.data() + base, records}
570 )
571 );
572 }
573 }
575 }
578}
#define CUDDL_CUDA_TRY(...)
Propagates a CUDA error wrapped in cuddl::Result<void>.
uint32_t warp_grid_blocks(size_t warps) noexcept
Blocks for a grid-stride launch covering warps warps of block_size threads.
@ host
Any node: for buffers host threads fill.
constexpr uint32_t block_size
Threads per CTA for the single-pair and cardinality reduction kernels.
__global__ top_k_slots slots
__global__ uint32_t uint32_t first_word
::cuda::__ensure_current_context device_scope
Makes stream's device current for the enclosing scope and restores the previous context on exit.
static __global__ uint32_t const * offsets
Result< void > Ok() noexcept
Success return for Result<void>; same as return {}.
uint32_t const * match_counts
Index match counts parallel to results, when requested; otherwise null.
__host__ __device__ constexpr uint32_t words_per_query() const noexcept
__host__ __device__ uint64_t slot_count() const noexcept
Slots of the tile's layout, filled or not.