3#include <cooperative_groups.h>
4#include <cooperative_groups/reduce.h>
5#include <cuda_runtime.h>
6#include <cub/block/block_histogram.cuh>
7#include <cub/block/block_reduce.cuh>
8#include <cub/block/block_scan.cuh>
9#include <cuda/std/algorithm>
10#include <cuda/std/bit>
11#include <cuda/std/cstddef>
12#include <cuda/std/cstdint>
23namespace cg = cooperative_groups;
57template <
bool IncludeCardinality>
58__device__ summary_payload combine_payloads(summary_payload a, summary_payload
const& b)
noexcept {
62 a.restored_sum += b.restored_sum;
73template <
typename QueryScore,
typename ReferenceScore>
75 (
sizeof(QueryScore) == 2U ||
sizeof(ReferenceScore) == 2U) ? 16U : 8U;
90template <
size_t BucketCount,
typename Layout = default_register_layout,
bool HasBlacklist = false>
92 uint64_t
const* input,
98 __shared__ uint32_t state[BucketCount];
99 for (
auto i = threadIdx.x; i < BucketCount; i += blockDim.x) {
104 auto const process = [&](uint64_t value) {
106 auto const incoming =
static_cast<uint32_t
>(score<Layout>(hash));
107 auto const bucket = bucket_of<BucketCount>(hash);
108 if constexpr (HasBlacklist) {
111 atomicMax(&state[bucket], incoming);
114 auto const stride =
static_cast<size_t>(gridDim.x) * blockDim.x * 4U;
115 auto const index = (
static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x) * 4U;
121 for (uint32_t item = 0; item < 4U; ++item) {
122 process(values[item]);
128 for (uint32_t item = 0; item < 4U; ++item) {
130 process(input[offset + item]);
144 auto const bucket_offset = (
static_cast<size_t>(blockIdx.x) * 139U) & (BucketCount - 1U);
145 for (
auto j = threadIdx.x; j < BucketCount; j += blockDim.x) {
146 auto const i = (
static_cast<size_t>(j) + bucket_offset) & (BucketCount - 1U);
147 auto const stored = state[i];
148 if (stored != 0U) atomicMax(&
registers[i], stored);
155template <
size_t BucketCount,
bool IncludeCardinality,
typename Layout = default_register_layout>
157summary_kernel(uint32_t
const* left, uint32_t
const* right,
pairwise_summary& output) {
158 using block_reduce = cub::BlockReduce<summary_payload, block_size>;
159 __shared__
typename block_reduce::TempStorage
storage;
162 for (
auto bucket =
static_cast<size_t>(threadIdx.x); bucket < BucketCount;
164 auto const left_reg = left[bucket];
165 auto const right_reg = right[bucket];
168 if (left_reg == 0U) {
171 local.restored_sum += restore_midpoint<Layout>(
static_cast<uint16_t
>(left_reg));
175 auto const total = block_reduce(
storage).Reduce(
local, combine_payloads<IncludeCardinality>);
176 if (threadIdx.x == 0) {
180 static_cast<double>(BucketCount),
181 static_cast<double>(
total.empty),
193template <
size_t BucketCount>
195 uint32_t
const* left_rows,
196 uint32_t
const* right_rows,
200 constexpr uint32_t warp_width = 32U;
202 using warp_reduce = cub::WarpReduce<pairwise_counts>;
205 auto const warp =
static_cast<uint32_t
>(threadIdx.x) / warp_width;
206 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
208 auto const pair_stride =
static_cast<size_t>(gridDim.x) *
warps_per_block;
209 for (; pair < pair_count; pair += pair_stride) {
210 auto const row_offset = pair * BucketCount;
212 for (
auto bucket =
static_cast<size_t>(
lane); bucket < BucketCount; bucket += warp_width) {
213 classify(
local, left_rows[row_offset + bucket], right_rows[row_offset + bucket]);
227inline __device__ pairwise_counts
228reduce_warp(cg::thread_block_tile<32>
const warp, pairwise_counts
counts) {
232 counts.both_empty = cg::reduce(
warp,
counts.both_empty, cg::plus<uint32_t>{});
242template <
typename Row>
243__global__
void transpose_indexed_scores_kernel(
247 uint32_t full_bucket_count,
250 constexpr uint32_t
tile = 32U;
251 __shared__ Row staging[
tile][
tile + 1U];
252 auto const bucket =
static_cast<uint32_t
>(blockIdx.x) *
tile + threadIdx.x;
253 auto const reference =
static_cast<uint32_t
>(blockIdx.y) *
tile + threadIdx.y;
255 staging[threadIdx.y][threadIdx.x] =
256 rows[
static_cast<size_t>(reference) * full_bucket_count + bucket];
263 auto const out_bucket =
static_cast<uint32_t
>(blockIdx.x) *
tile + threadIdx.y;
264 auto const out_reference =
static_cast<uint32_t
>(blockIdx.y) *
tile + threadIdx.x;
266 transposed[
static_cast<size_t>(out_bucket) *
reference_count + out_reference] =
267 staging[threadIdx.x][threadIdx.y];
278template <
typename Row>
279__global__
void count_index_cells_bucket_kernel(
280 Row
const* transposed,
284 uint32_t* cell_counts
286 extern __shared__ uint32_t key_counts[];
288 auto const quarter_keys =
key_count / 4U;
290 if (blockIdx.x == 0U && threadIdx.x == 0U) {
293 for (
auto i = threadIdx.x; i < quarter_keys; i += blockDim.x) {
299 auto const* bucket_scores = transposed +
static_cast<size_t>(bucket) *
reference_count;
300 auto* bucket_counts = cell_counts +
static_cast<size_t>(bucket) *
key_count;
301 for (uint32_t quarter = 0U; quarter < 4U; ++quarter) {
302 auto const key_base = quarter * quarter_keys;
304 reference += blockDim.x) {
305 auto const score =
static_cast<uint16_t
>(__ldcs(&bucket_scores[reference]));
310 if (key < key_base || key >= key_base + quarter_keys) {
313 atomicAdd(&key_counts[key - key_base], 1U);
316 for (
auto i = threadIdx.x; i < quarter_keys; i += blockDim.x) {
317 bucket_counts[key_base + i] = key_counts[i];
334template <
typename Row>
335__global__
void scatter_index_postings_bucket_kernel(
336 Row
const* transposed,
343 extern __shared__ uint32_t key_ranks[];
345 auto const quarter_keys =
key_count / 4U;
346 for (
auto i = threadIdx.x; i < quarter_keys; i += blockDim.x) {
352 auto const* bucket_scores = transposed +
static_cast<size_t>(bucket) *
reference_count;
353 auto const* bucket_offsets =
offsets +
static_cast<size_t>(bucket) *
key_count;
354 for (uint32_t quarter = 0U; quarter < 4U; ++quarter) {
355 auto const key_base = quarter * quarter_keys;
357 reference += blockDim.x) {
358 auto const score =
static_cast<uint16_t
>(__ldcs(&bucket_scores[reference]));
363 if (key < key_base || key >= key_base + quarter_keys) {
366 auto const rank = atomicAdd(&key_ranks[key - key_base], 1U);
367 __stcs(&
postings[bucket_offsets[key] + rank], reference);
370 for (
auto i = threadIdx.x; i < quarter_keys; i += blockDim.x) {
379struct minimum_match_predicate {
383 [[nodiscard]] __host__ __device__
bool operator()(uint32_t reference_id)
const noexcept {
404 auto const cell =
static_cast<size_t>(bucket) *
key_count + key;
408 auto const sparse_key =
static_cast<uint16_t
>(key + (
key_count == 32768U));
409 auto const range = cuda::std::equal_range(first, first +
reference_count, sparse_key);
421 uint32_t cell_bits = 0U;
422 while (cell_bits < 16U && (uint64_t{1} << (cell_bits + 1U)) * 8U <=
reference_count) {
425 return 16U - cell_bits;
445 i +=
static_cast<size_t>(gridDim.x) *
block_size) {
446 auto const bucket = i / entries;
447 auto const bound =
static_cast<uint32_t
>(i % entries) <<
shift;
450 cuda::std::lower_bound(
454 [](uint16_t key, uint32_t value) {
return key < value; }
479 for (
auto i =
static_cast<size_t>(blockIdx.x) *
block_size + threadIdx.x; i <
total;
480 i +=
static_cast<size_t>(gridDim.x) *
block_size) {
481 uint64_t length = 0U;
489 auto const key = first[position];
490 if (key != 0U && (position == 0U || first[position - 1U] != key)) {
491 length =
static_cast<uint64_t
>(
492 cuda::std::upper_bound(first + position, first +
reference_count, key) -
497 local += length * length;
516 auto const*
low = first +
cells[cell];
517 auto const*
high = first +
cells[cell + 1U];
518 auto const less = [](uint16_t key, uint32_t value) {
521 auto const* begin = cuda::std::lower_bound(
low,
high, sparse_key, less);
522 auto const* end = cuda::std::lower_bound(begin,
high, sparse_key + 1U, less);
535 uint16_t
const*
query,
544 using block_scan = cub::BlockScan<uint32_t, posting_range_block_size>;
545 __shared__
typename block_scan::TempStorage
storage;
549 auto const bucket = base +
static_cast<uint32_t
>(threadIdx.x);
558 uint32_t aggregate = 0U;
559 block_scan(
storage).InclusiveSum(range.y - range.x, end, aggregate);
579 for (
auto item =
static_cast<uint32_t
>(blockIdx.x) *
block_size + threadIdx.x; item <
total;
581 auto const bucket =
static_cast<uint32_t
>(
585 auto const first_item = bucket == 0U ? 0U :
range_end_scan[bucket - 1U];
596template <
size_t BucketCount,
typename QueryRow>
598 QueryRow
const* queries,
609 constexpr uint32_t warp_width = 32;
611 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
612 auto const warp =
static_cast<uint32_t
>(threadIdx.x) / warp_width;
619 auto const first_cell =
621 auto const warp_cell_stride =
623 for (
auto cell_base = first_cell; cell_base < total_cells; cell_base += warp_cell_stride) {
624 auto const cell = cell_base +
lane;
625 auto const query_index =
static_cast<uint32_t
>(cell >> bucket_shift);
626 auto const bucket =
static_cast<uint32_t
>(cell) & bucket_mask;
629 ?
static_cast<uint16_t
>(
644 auto pending = __ballot_sync(0xffffffffU, range.x < range.y);
645 while (pending != 0U) {
646 auto const source =
static_cast<uint32_t
>(__ffs(pending) - 1);
647 pending &= pending - 1U;
648 auto const end = __shfl_sync(0xffffffffU, range.y, source);
649 auto posting = __shfl_sync(0xffffffffU, range.x, source) +
lane;
655 for (; posting + 3U * warp_width < end; posting += 4U * warp_width) {
658 for (uint32_t j = 0U; j < 4U; ++j) {
662 for (uint32_t j = 0U; j < 4U; ++j) {
666 for (; posting < end; posting += warp_width) {
674__host__ __device__
constexpr uint32_t candidate_bit_words(uint32_t
reference_count)
noexcept {
679constexpr uint32_t index_tile_block_size = 1024U;
705 uint32_t multiprocessors,
706 uint32_t max_shared_bytes
709 auto const max_references = (max_shared_bytes - static_bytes) / (64U *
sizeof(uint16_t)) * 64U;
710 auto const needed = (
reference_count + max_references - 1U) / max_references;
712 auto const tiles = needed > filling ? needed : filling;
728 return key != 0U && (position == 0U ||
sorted_keys[cell - 1U] != key) &&
735static __global__
void build_posting_bitmaps_kernel(
745 __shared__ uint2 range;
746 if (threadIdx.x == 0U) {
747 auto const cell =
cells[blockIdx.x];
756 static_cast<uint32_t
>(
765 for (
auto i =
static_cast<size_t>(range.x) + threadIdx.x; i < range.y; i += blockDim.x) {
767 atomicOr(
bitmap + reference / 32U, 1U << (reference % 32U));
774template <
size_t BucketCount>
776 uint16_t
const* queries,
778 uint32_t
const* bitmap_cells,
779 uint32_t bitmap_count,
781 uint32_t indexed_buckets,
787 using block_scan = cub::BlockScan<uint32_t, block_size>;
788 __shared__
typename block_scan::TempStorage
storage;
790 auto const query =
static_cast<uint32_t
>(blockIdx.x);
791 auto const base =
static_cast<size_t>(
query) * indexed_buckets;
792 for (uint32_t first = 0U; first < indexed_buckets; first +=
block_size) {
793 auto const bucket = first + threadIdx.x;
796 bool selected =
false;
797 auto const score = bucket < indexed_buckets
798 ? queries[
static_cast<size_t>(
query) * BucketCount + bucket]
805 range =
ranges[base + bucket];
807 if (range.y - range.x >=
words) {
808 uint32_t
low = 0U,
high = bitmap_count;
811 if (bitmap_cells[middle] < cell) {
822 uint32_t position = 0U, aggregate = 0U;
823 block_scan(
storage).ExclusiveSum(
static_cast<uint32_t
>(selected), position, aggregate);
827 if (bucket < indexed_buckets) {
828 ranges[base + bucket] = range;
833 if (threadIdx.x == 0U) {
843template <u
int32_t CounterBits>
846 uint32_t
const* starts,
851 uint32_t (&counters)[CounterBits]
853 constexpr uint32_t batch = 8U;
854 for (uint32_t i = 0U; i <
count && passed != ~0U; i += batch) {
856 uint32_t loaded[batch]{};
858 for (uint32_t j = 0U; j < batch; ++j) {
864 auto const sum0 = loaded[0] ^ loaded[1] ^ loaded[2];
865 auto const carry0 = (loaded[0] & loaded[1]) | ((loaded[0] | loaded[1]) & loaded[2]);
866 auto const sum1 = loaded[3] ^ loaded[4] ^ loaded[5];
867 auto const carry1 = (loaded[3] & loaded[4]) | ((loaded[3] | loaded[4]) & loaded[5]);
868 auto const ones0 = sum0 ^ sum1 ^ loaded[6];
869 auto const carry2 = (sum0 & sum1) | ((sum0 | sum1) & loaded[6]);
870 auto const twos0 = carry0 ^ carry1 ^ carry2;
871 auto const fours0 = (carry0 & carry1) | ((carry0 | carry1) & carry2);
872 auto const carry3 = ones0 & loaded[7];
873 auto const carry4 = twos0 & carry3;
874 uint32_t
const additions[]{
875 ones0 ^ loaded[7], twos0 ^ carry3, fours0 ^ carry4, fours0 & carry4
878 uint32_t at_least = ~0U;
880 for (uint32_t bit = 0U; bit < CounterBits; ++bit) {
881 auto const extra = bit < 4U ? additions[bit] : 0U;
882 auto const previous = counters[bit];
883 counters[bit] = previous ^
carry ^ extra;
886 at_least = (counters[bit] & ~key) | ((counters[bit] | ~key) & at_least);
889 passed |= at_least |
carry;
891 for (uint32_t bit = CounterBits; bit < 4U; ++bit) {
892 passed |= additions[bit];
913template <
size_t BucketCount, u
int32_t CounterBits,
typename QueryRow>
915 QueryRow const* queries,
934 constexpr uint32_t warp_width = 32;
946 for (
auto i = threadIdx.x; i < (
high -
low + warp_width - 1U) / warp_width;
958 for (
auto i = threadIdx.x; i < (first -
low) / warp_width; i += blockDim.x) {
966 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
967 auto const warp =
static_cast<uint32_t
>(threadIdx.x) / warp_width;
968 if constexpr (CounterBits != 0U) {
971 auto const tile_words = (
width + warp_width - 1U) / warp_width;
975 for (
auto base =
warp * warp_width; base < tile_words; base += blockDim.x) {
977 uint32_t counters[CounterBits]{};
978 uint32_t passed = ~0U;
979 if (base +
lane < tile_words) {
982 uint32_t excluded = 0U;
998 for (uint32_t pair = 0U; pair < warp_width / 2U; ++pair) {
999 auto const source = 2U * pair +
lane / 16U;
1002 for (uint32_t plane = 0U; plane < CounterBits; ++plane) {
1004 (__shfl_sync(0xffffffffU, counters[plane], source) >>
shift) & 3U;
1007 auto const pass = __shfl_sync(0xffffffffU, passed, source) >>
shift;
1014 auto const slot = (base + source) * 16U +
lane % 16U;
1015 if (slot < (
width + 1U) / 2U) {
1021 for (
auto i = threadIdx.x; i < (
width + 1U) / 2U; i += blockDim.x) {
1025 if (threadIdx.x == 0U) {
1031 auto const count = [&](uint32_t reference) {
1032 auto const slot = reference -
low;
1033 atomicAdd(&
packed_counts[slot / 2U], 1U << ((slot % 2U) * 16U));
1039 auto const bucket = base +
lane;
1041 bucket < indexed_bucket_count ? static_cast<uint16_t>(
query[bucket]) : 0U;
1042 uint32_t begin = 0U;
1062 begin =
static_cast<uint32_t
>(
1067 end =
static_cast<uint32_t
>(
1078 auto posting = begin;
1080 for (; end - posting >= 4U; posting += 4U) {
1083 for (uint32_t j = 0U; j < 4U; ++j) {
1087 for (uint32_t j = 0U; j < 4U; ++j) {
1097 for (; posting < end; ++posting) {
1098 auto const reference =
postings[posting];
1099 if (reference >=
high) {
1102 if (reference >=
low) {
1110 if (end - begin >= index_tile_block_size) {
1117 while (pending != 0U) {
1118 auto const source =
static_cast<uint32_t
>(__ffs(pending) - 1);
1119 pending &= pending - 1U;
1120 auto const range_end = __shfl_sync(0xffffffffU, end, source);
1121 auto posting = __shfl_sync(0xffffffffU, begin, source) +
lane;
1122 for (; posting + 3U * warp_width < range_end; posting += 4U * warp_width) {
1125 for (uint32_t j = 0U; j < 4U; ++j) {
1129 for (uint32_t j = 0U; j < 4U; ++j) {
1134 auto const remaining = posting < range_end ? range_end - posting : 0U;
1137 for (uint32_t j = 0U; j < 3U; ++j) {
1138 if (j * warp_width < remaining) {
1143 for (uint32_t j = 0U; j < 3U; ++j) {
1144 if (j * warp_width < remaining) {
1156 auto posting = range.x +
static_cast<uint32_t
>(threadIdx.x);
1157 for (; posting + 3U * index_tile_block_size < range.y;
1158 posting += 4U * index_tile_block_size) {
1161 for (uint32_t j = 0U; j < 4U; ++j) {
1162 ids[j] =
postings[posting + j * index_tile_block_size];
1165 for (uint32_t j = 0U; j < 4U; ++j) {
1169 for (; posting < range.y; posting += index_tile_block_size) {
1177 for (
auto i = threadIdx.x; i <
width; i += blockDim.x) {
1187 for (
auto base =
warp * warp_width; base <
width; base += index_tile_block_size) {
1188 auto const i = base +
lane;
1189 auto selected =
false;
1195 auto const word = __ballot_sync(0xffffffffU, selected);
1206template <
size_t BucketCount,
typename QueryRow>
1208 QueryRow
const* queries,
1218 auto const bucket =
static_cast<uint32_t
>(blockIdx.x);
1224 uint2 range{0U, 0U};
1240template <
typename ReferenceScore>
1243 uint32_t
const (&q)[8],
1244 ReferenceScore
const* reference
1246 auto const* qs =
reinterpret_cast<uint16_t const*
>(q);
1247 if constexpr (
sizeof(ReferenceScore) == 2U) {
1253 uint32_t lower = 0U;
1254 uint32_t higher = 0U;
1255 uint32_t empty = 0U;
1257 for (uint32_t i = 0; i < 8U; ++i) {
1258 auto const q_low = q[i] & 0xffffU;
1259 auto const q_high = q[i] >> 16U;
1260 auto const r_low = r[i] & 0xffffU;
1261 auto const r_high = r[i] >> 16U;
1262 lower += ((q_low - r_low) >> 31U) + ((q_high - r_high) >> 31U);
1263 higher += ((r_low - q_low) >> 31U) + ((r_high - q_high) >> 31U);
1264 empty += ((q_low + r_low - 1U) >> 31U) + ((q_high + r_high - 1U) >> 31U);
1266 target.
lower += lower;
1269 target.
equal += 16U - lower - higher - empty;
1276 for (uint32_t i = 0; i < 8U; ++i) {
1280 for (uint32_t i = 0; i < 8U; ++i) {
1281 classify(target, qs[8U + i], r1[i]);
1289template <
typename SearchResult>
1293 uint32_t reference_id,
1303 .
lower = __reduce_add_sync(0xffffffffU,
local.lower),
1304 .equal = __reduce_add_sync(0xffffffffU,
local.equal),
1305 .higher = __reduce_add_sync(0xffffffffU,
local.higher),
1306 .both_empty = __reduce_add_sync(0xffffffffU,
local.both_empty),
1310 results[index].reference_id = reference_id;
1335 return (((
group / 32U) * 4U + plane / 4U) * 32U +
group % 32U) * 4U + plane % 4U;
1339__device__ __forceinline__
void
1343 for (uint32_t plane = 0; plane <
score_planes; ++plane) {
1344 auto const word = __ballot_sync(0xffffffffU, ((
score >> plane) & 1U) != 0U);
1345 if (
lane == plane) {
1355template <
size_t BucketCount>
1359 constexpr uint32_t warp_width = 32;
1361 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
1363 auto const stride =
static_cast<size_t>(gridDim.x) * (
block_size / warp_width);
1364 for (
auto item = (
static_cast<size_t>(blockIdx.x) *
block_size + threadIdx.x) / warp_width;
1379__device__ __forceinline__
void
1383 auto const v =
row[((
group / 32U) * 4U + c) * 32U +
group % 32U];
1384 words[4U * c] = v.x;
1385 words[4U * c + 1U] = v.y;
1386 words[4U * c + 2U] = v.z;
1387 words[4U * c + 3U] = v.w;
1404 template <
size_t BucketCount,
bool ReferenceFull = false>
1406 auto const total_lower = __reduce_add_sync(0xffffffffU,
lower);
1407 auto const total_differ = __reduce_add_sync(0xffffffffU,
differ);
1408 auto const total_occupied = ReferenceFull ?
static_cast<uint32_t
>(BucketCount)
1409 : __reduce_add_sync(0xffffffffU,
occupied);
1411 .lower = total_lower,
1412 .equal = total_occupied - total_differ,
1413 .higher = total_differ - total_lower,
1414 .both_empty =
static_cast<uint32_t
>(BucketCount) - total_occupied,
1420[[nodiscard]] __device__ __forceinline__ uint32_t
1431[[nodiscard]] __device__ __forceinline__ uint32_t
1456template <
bool CountMatches,
bool ReferenceFull = false>
1460 uint32_t reference_any,
1461 uint32_t reference_low_any,
1463 uint32_t match_lanes,
1468 uint32_t low_ne = 0U;
1474 lt = (~q[p] & r[p]) | (~(q[p] ^ r[p]) & lt);
1477 counts.lower +=
static_cast<uint32_t
>(__popc(lt));
1478 counts.differ +=
static_cast<uint32_t
>(__popc(ne));
1479 if constexpr (!ReferenceFull) {
1480 counts.occupied +=
static_cast<uint32_t
>(__popc(ne | reference_any));
1482 if constexpr (CountMatches) {
1487 auto const top_keyed = 0U - (
static_cast<uint32_t
>(
key_mask) >> top);
1488 auto const key_differs = (ne & top_keyed) | (low_ne & ~top_keyed);
1489 counts.matches +=
static_cast<uint32_t
>(
1490 __popc(~key_differs & (reference_low_any | (q[top] & r[top])) & match_lanes)
1496template <
size_t BucketCount>
1500 constexpr uint32_t warp_width = 32;
1501 constexpr uint32_t
groups_per_row =
static_cast<uint32_t
>(BucketCount / warp_width);
1502 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
1504 auto const stride =
static_cast<size_t>(gridDim.x) * (
block_size / warp_width);
1505 for (
auto item = (
static_cast<size_t>(blockIdx.x) *
block_size + threadIdx.x) / warp_width;
1514 uint32_t
score = 0U;
1526 return static_cast<uint32_t
>(
1536template <
size_t BucketCount,
typename SearchResult>
1538 uint32_t
const* query_planes,
1545 constexpr uint32_t warp_width = 32;
1546 constexpr uint32_t
row_words =
static_cast<uint32_t
>(BucketCount / 2U);
1547 constexpr uint32_t
groups_per_row =
static_cast<uint32_t
>(BucketCount / warp_width);
1548 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
1550 auto const stride =
static_cast<uint32_t
>(gridDim.x) * (
block_size / warp_width);
1551 auto const*
query =
reinterpret_cast<uint4 const*
>(query_planes);
1552 for (
auto item = (
static_cast<uint32_t
>(blockIdx.x) *
block_size + threadIdx.x) / warp_width;
1556 auto const* reference =
reinterpret_cast<uint4 const*
>(
1569 results[item].reference_id = reference_id;
1579template <
size_t BucketCount>
1581 uint32_t
const* query_planes,
1589 constexpr uint32_t warp_width = 32;
1590 constexpr uint32_t
row_words =
static_cast<uint32_t
>(BucketCount / 2U);
1591 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
1593 auto const stride =
static_cast<uint64_t
>(gridDim.x) * (
block_size / warp_width);
1595 for (
auto pair = (
static_cast<uint64_t
>(blockIdx.x) *
block_size + threadIdx.x) / warp_width;
1601 auto const* reference =
1611 auto const total = __reduce_add_sync(0xffffffffU,
counts.matches);
1630 for (
auto item =
static_cast<size_t>(blockIdx.x) *
block_size + threadIdx.x; item <
total;
1631 item +=
static_cast<size_t>(gridDim.x) *
block_size) {
1633 auto const first =
static_cast<uint32_t
>(item %
words) * 32U;
1635 for (uint32_t bit = 0U; bit < 32U && first + bit <
reference_count; ++bit) {
1636 auto const reference_id = first + bit;
1637 auto const selected =
1641 bits |=
static_cast<uint32_t
>(selected) << bit;
1662template <
size_t BucketCount>
1669template <
size_t BucketCount>
1671 bitmap_refine_staged<BucketCount>
1672 ?
static_cast<uint32_t
>(cuda::std::min<size_t>(
1679template <
size_t BucketCount>
1681 bitmap_refine_staged<BucketCount>
1682 ?
static_cast<size_t>(bitmap_refine_group<BucketCount>) * BucketCount *
sizeof(uint16_t)
1689__device__ __forceinline__ uint32_t
1692 return cell + gridDim.x;
1695 if (threadIdx.x == 0U) {
1716template <
bool UpperTriangle>
1729 auto const blocks_per_group = cuda::std::max(1U, waves * gridDim.x /
groups);
1730 auto const block_words = cuda::std::max(
1750template <
bool UpperTriangle>
1753 uint32_t reference_id,
1757 if constexpr (UpperTriangle) {
1770 return static_cast<uint32_t
>(cuda::std::popcount(
word));
1780template <
bool UpperTriangle,
typename SearchResult>
1794 constexpr uint32_t warp_width = 32;
1796 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
1800 w +=
static_cast<size_t>(gridDim.x) * (
block_size / warp_width)) {
1810 static_cast<uint32_t
>(__popc(
bits & ((1U <<
lane) - 1U)));
1838 typename SearchResult,
1841 uint32_t QueryGroup>
1843 uint32_t
const* query_planes,
1857 constexpr uint32_t warp_width = 32;
1859 static_assert(
group >= 1U &&
group <= warp_width);
1860 constexpr bool staged = bitmap_refine_staged<BucketCount>;
1861 constexpr uint32_t
row_words =
static_cast<uint32_t
>(BucketCount / 2U);
1862 constexpr uint32_t
groups_per_row =
static_cast<uint32_t
>(BucketCount / warp_width);
1876 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
1878 auto const cells = make_refine_cells<UpperTriangle>(
1891 cell <
cells.cell_count;
1897 auto const my_query =
lane < group_size ? first_query +
lane : first_query;
1898 auto const* group_planes =
reinterpret_cast<uint4 const*
>(
1899 query_planes +
static_cast<size_t>(first_query) *
row_words
1903 if (threadIdx.x == 0U) {
1907 constexpr uint32_t member_vectors =
row_words / 4U;
1908 for (
auto i =
static_cast<uint32_t
>(threadIdx.x); i < group_size * member_vectors;
1911 query_planes +
static_cast<size_t>(first_query + i / member_vectors) *
row_words
1912 )[i % member_vectors];
1920 uint32_t claim = 0U;
1924 claim = __shfl_sync(0xffffffffU, claim, 0);
1926 if (
word >= last_word) {
1931 if (
lane < group_size) {
1932 constexpr uint32_t slice_width = warp_width /
word_slices;
1933 constexpr uint32_t slice_mask =
1934 slice_width == warp_width ? ~0U : (1U << slice_width) - 1U;
1937 (slice_mask << (claim %
word_slices * slice_width));
1939 auto const first_reference =
word * warp_width;
1941 bits = left >= warp_width ? ~0U : (1U << left) - 1U;
1942 if constexpr (UpperTriangle) {
1951 (slice_mask << (claim %
word_slices * slice_width));
1956 uint32_t passed = 0U;
1957 auto remaining = __reduce_or_sync(0xffffffffU,
bits);
1958 while (remaining != 0U) {
1959 auto const bit =
static_cast<uint32_t
>(__ffs(remaining) - 1);
1960 remaining &= remaining - 1U;
1961 auto const reference_id =
word * warp_width + bit;
1962 auto const selected = __ballot_sync(0xffffffffU, ((
bits >> bit) & 1U) != 0U);
1963 auto const* reference =
reinterpret_cast<uint4 const*
>(
1975 uint32_t reference_nonzero = ~0U;
1978 reference_nonzero &= r_any[j];
1981 __all_sync(0xffffffffU, reference_nonzero == ~0U);
1984 uint32_t mine_lower = 0U;
1985 uint32_t mine_equal = 0U;
1986 uint32_t mine_empty = 0U;
1987 uint32_t mine_matches = 0U;
1988 auto const compare_members = [&](
auto full) {
1989 auto const compare_member = [&](uint32_t m) {
1990 auto const* member =
1991 member_planes +
static_cast<size_t>(m) * (
row_words / 4U);
1993 auto const compare = [&](uint32_t bucket_group,
1996 uint32_t rg_low_any) {
1999 compare_plane_group<CountMatches, decltype(full)::value>(
2011 compare(j * warp_width +
lane, r[j], r_any[j], r_low_any[j]);
2017 j * warp_width +
lane,
2023 auto const total =
counts.reduce<BucketCount,
decltype(full)::value>();
2024 uint32_t counts_matches_total = 0U;
2025 if constexpr (CountMatches) {
2026 counts_matches_total = __reduce_add_sync(0xffffffffU,
counts.matches);
2029 mine_lower =
total.lower;
2030 mine_equal =
total.equal;
2031 mine_empty =
total.both_empty;
2032 mine_matches = counts_matches_total;
2037 auto const member_count =
static_cast<uint32_t
>(__popc(selected));
2038 for (uint32_t m = 0U; m < member_count; ++m) {
2042 for (
auto members = selected; members != 0U; members &= members - 1U) {
2043 compare_member(
static_cast<uint32_t
>(__ffs(members) - 1));
2047 if (reference_full) {
2048 compare_members(cuda::std::true_type{});
2050 compare_members(cuda::std::false_type{});
2053 if (((selected >>
lane) & 1U) != 0U && keep) {
2054 auto const slot = batch_result_slot<UpperTriangle>(
2058 results[slot] = SearchResult::pack(
2061 static_cast<uint32_t
>(BucketCount) - mine_lower - mine_equal - mine_empty
2063 if constexpr (CountMatches) {
2068 passed |= 1U << bit;
2094template <
size_t BucketCount>
2096 bitmap_refine_staged<BucketCount> && BucketCount % 1024U == 0U && BucketCount <= 2048U;
2100__device__ __forceinline__
void
2102 auto const value =
row[((
group / 32U) * 4U + chunk) * 32U +
group % 32U];
2124template <
size_t BucketCount,
bool UpperTriangle, u
int32_t QueryGroup>
2126 uint32_t const* query_planes,
2136 static_assert(match_filter_supported<BucketCount>);
2137 constexpr uint32_t warp_width = 32;
2138 constexpr uint32_t
group = QueryGroup;
2139 static_assert(
group >= 1U &&
group <= warp_width);
2140 constexpr uint32_t
row_words =
static_cast<uint32_t
>(BucketCount / 2U);
2141 constexpr uint32_t
lane_groups =
static_cast<uint32_t
>(BucketCount / warp_width / warp_width);
2142 constexpr uint32_t
max_block_words =
static_cast<uint32_t
>(cuda::std::max<size_t>(
2145 extern __shared__ uint4 filter_query_planes[];
2149 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
2151 auto const cells = make_refine_cells<UpperTriangle>(
2164 cell <
cells.cell_count;
2170 auto const* group_planes =
reinterpret_cast<uint4 const*
>(
2171 query_planes +
static_cast<size_t>(first_query) *
row_words
2175 if (threadIdx.x == 0U) {
2178 for (
auto i =
static_cast<uint32_t
>(threadIdx.x); i < group_size * (
row_words / 4U);
2180 filter_query_planes[i] = group_planes[i];
2189 word = __shfl_sync(0xffffffffU,
word, 0);
2190 if (
word >= last_word) {
2195 if (
lane < group_size) {
2196 auto const first_reference =
word * warp_width;
2198 bits = left >= warp_width ? ~0U : (1U << left) - 1U;
2199 if constexpr (UpperTriangle) {
2207 uint32_t passed = 0U;
2208 auto remaining = __reduce_or_sync(0xffffffffU,
bits);
2209 constexpr uint32_t refs = 4U;
2212 while (remaining != 0U) {
2215 uint32_t last_bit = 0U;
2217 for (uint32_t i = 0; i < refs; ++i) {
2218 has[i] = remaining != 0U;
2219 bit[i] = has[i] ?
static_cast<uint32_t
>(__ffs(remaining) - 1) : bit[0];
2220 last_bit = has[i] ? bit[i] : last_bit;
2221 remaining &= remaining - 1U;
2223 uint4
const* reference[refs];
2224 uint32_t selecting[refs];
2227 for (uint32_t i = 0; i < refs; ++i) {
2228 reference[i] =
reinterpret_cast<uint4 const*
>(
2232 selecting[i] = has[i] ? __ballot_sync(0xffffffffU, (
bits >> bit[i]) & 1U) : 0U;
2236 for (uint32_t c = 0; c < bound_chunks; ++c) {
2240 for (uint32_t w = 0; w < 4U; ++w) {
2241 r[i][j][4U * c + w] =
words[w];
2247 auto const members =
static_cast<uint32_t
>(
2248 __popc(__ballot_sync(0xffffffffU, (
bits >> last_bit) & 1U))
2250 for (uint32_t m = 0U; m < members; ++m) {
2251 auto const* member =
2252 filter_query_planes +
static_cast<size_t>(m) * (
row_words / 4U);
2253 uint32_t mismatches[refs] = {};
2256 uint32_t differ[refs];
2258 for (uint32_t i = 0; i < refs; ++i) {
2259 differ[i] = excluded_lanes[j];
2262 for (uint32_t c = 0; c < bound_chunks; ++c) {
2266 for (uint32_t i = 0; i < refs; ++i) {
2268 for (uint32_t w = 0; w < 4U; ++w) {
2269 differ[i] |= q[w] ^ r[i][j][4U * c + w];
2274 for (uint32_t i = 0; i < refs; ++i) {
2275 mismatches[i] +=
static_cast<uint32_t
>(__popc(differ[i]));
2279 for (uint32_t i = 0; i < refs; ++i) {
2280 auto const total_bound =
static_cast<uint32_t
>(BucketCount) -
2281 __reduce_add_sync(0xffffffffU, mismatches[i]);
2282 if (
lane == m && ((selecting[i] >> m) & 1U) != 0U &&
2284 passed |= 1U << bit[i];
2289 if (
lane < group_size) {
2315template <
size_t BucketCount,
typename SearchResult,
bool UpperTriangle, u
int32_t QueryGroup>
2317 uint32_t const* query_planes,
2326 static_assert(match_filter_supported<BucketCount>);
2327 constexpr uint32_t warp_width = 32;
2328 constexpr uint32_t
group = QueryGroup;
2329 static_assert(
group >= 1U &&
group <= warp_width);
2330 constexpr uint32_t
row_words =
static_cast<uint32_t
>(BucketCount / 2U);
2331 constexpr uint32_t
lane_groups =
static_cast<uint32_t
>(BucketCount / warp_width / warp_width);
2332 constexpr uint32_t
max_block_words =
static_cast<uint32_t
>(cuda::std::max<size_t>(
2335 extern __shared__ uint4 all_pairs_query_planes[];
2340 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
2342 auto const cells = make_refine_cells<UpperTriangle>(
2348 cell <
cells.cell_count;
2354 auto const* group_planes =
reinterpret_cast<uint4 const*
>(
2355 query_planes +
static_cast<size_t>(first_query) *
row_words
2359 if (threadIdx.x == 0U) {
2362 for (
auto i =
static_cast<uint32_t
>(threadIdx.x); i < group_size * (
row_words / 4U);
2364 all_pairs_query_planes[i] = group_planes[i];
2373 word = __shfl_sync(0xffffffffU,
word, 0);
2376 if (
word >= last_word) {
2381 if (
lane < group_size) {
2382 auto const first_reference =
word * warp_width;
2384 bits = left >= warp_width ? ~0U : (1U << left) - 1U;
2385 bits &= ((1U << part_width) - 1U) << (part * part_width);
2386 if constexpr (UpperTriangle) {
2397 auto remaining = __reduce_or_sync(0xffffffffU,
bits);
2398 while (remaining != 0U) {
2399 auto const bit_a =
static_cast<uint32_t
>(__ffs(remaining) - 1);
2400 remaining &= remaining - 1U;
2401 auto const has_b = remaining != 0U;
2402 auto const bit_b = has_b ?
static_cast<uint32_t
>(__ffs(remaining) - 1) : bit_a;
2403 remaining &= remaining - 1U;
2404 auto const reference_a =
word * warp_width + bit_a;
2405 auto const reference_b =
word * warp_width + bit_b;
2406 auto const* planes_a =
reinterpret_cast<uint4 const*
>(
2409 auto const* planes_b =
reinterpret_cast<uint4 const*
>(
2416 uint32_t nonzero = ~0U;
2423 nonzero &= a_any[j] & b_any[j];
2426 auto const selected_a = __ballot_sync(0xffffffffU, (
bits >> bit_a) & 1U);
2427 auto const selected_b = __ballot_sync(0xffffffffU, (
bits >> bit_b) & 1U);
2428 auto const members =
static_cast<uint32_t
>(__popc(selected_b));
2432 auto const compare = [&](
auto full) {
2433 for (uint32_t m = 0U; m < members; ++m) {
2434 auto const* member =
2435 all_pairs_query_planes +
static_cast<size_t>(m) * (
row_words / 4U);
2442 compare_plane_group<false, decltype(full)::value>(
2443 q, a[j], a_any[j], 0U, 0U, 0U, counts_a
2445 compare_plane_group<false, decltype(full)::value>(
2446 q, b[j], b_any[j], 0U, 0U, 0U, counts_b
2449 auto const total_a = counts_a.reduce<BucketCount,
decltype(full)::value>();
2450 auto const total_b = counts_b.reduce<BucketCount,
decltype(full)::value>();
2457 if (__all_sync(0xffffffffU, nonzero == ~0U)) {
2458 compare(cuda::std::true_type{});
2460 compare(cuda::std::false_type{});
2463 if (((selected_a >>
lane) & 1U) != 0U) {
2464 results[batch_result_slot<UpperTriangle>(
2466 )] = SearchResult::pack(mine_a.lower, mine_a.equal, mine_a.higher);
2468 if (has_b && ((selected_b >>
lane) & 1U) != 0U) {
2469 results[batch_result_slot<UpperTriangle>(
2471 )] = SearchResult::pack(mine_b.lower, mine_b.equal, mine_b.higher);
2484template <
size_t BucketCount,
typename SearchResult,
bool UpperTriangle,
bool Swar>
2486 uint16_t
const*
rows,
2493 constexpr uint32_t warp_width = 32;
2494 auto const lane =
static_cast<uint32_t
>(threadIdx.x) % warp_width;
2497 for (
auto pair = (uint64_t{blockIdx.x} *
block_size + threadIdx.x) / warp_width; pair <
pairs;
2500 auto const reference_id =
static_cast<uint32_t
>(pair %
reference_count);
2502 if (UpperTriangle && reference_id <=
query_id)
continue;
2504 ((
reference_mask[reference_id / 32U] >> (reference_id % 32U)) & 1U) == 0U) {
2507 auto const* query_row =
rows +
size_t{
query_id} * BucketCount;
2508 auto const* reference_row =
rows +
size_t{reference_id} * BucketCount;
2509 pairwise_counts
counts{};
2510 if constexpr (Swar) {
2511 for (
auto chunk =
lane; chunk < BucketCount / 16U; chunk += warp_width) {
2517 for (
auto bucket =
lane; bucket < BucketCount; bucket += warp_width) {
2521 auto const lower = __reduce_add_sync(0xffffffffU,
counts.lower);
2522 auto const equal = __reduce_add_sync(0xffffffffU,
counts.equal);
2523 auto const higher = __reduce_add_sync(0xffffffffU,
counts.higher);
2525 results[batch_result_slot<UpperTriangle>(
2527 )] = SearchResult::pack(lower, equal, higher);
2540 return {left.
empty + right.empty, left.restored + right.restored};
2549template <
size_t BucketCount,
typename Layout = default_register_layout>
2552 using block_reduce = cub::BlockReduce<cardinality_payload, block_size>;
2553 __shared__
typename block_reduce::TempStorage
storage;
2555 for (
auto bucket =
static_cast<size_t>(threadIdx.x); bucket < BucketCount;
2556 bucket += blockDim.x) {
2557 auto const stored =
static_cast<uint16_t
>(__ldcs(&
registers[bucket]));
2561 local.restored +=
static_cast<float>(restore_midpoint<Layout>(stored));
2565 if (threadIdx.x == 0) {
2566 *empty_out =
total.empty;
2568 static_cast<float>(BucketCount),
static_cast<float>(
total.empty),
total.restored
2574template <
size_t BucketCount,
typename Layout = default_register_layout>
2579 __shared__ uint32_t bins[
nlz_bins];
2580 using block_histogram =
2581 cub::BlockHistogram<uint32_t, block_size, 1, nlz_bins, cub::BLOCK_HISTO_ATOMIC>;
2582 using block_reduce = cub::BlockReduce<float, block_size>;
2583 __shared__
typename block_reduce::TempStorage
storage;
2584 block_histogram histogram;
2585 histogram.InitHistogram(bins);
2586 float local_restored = 0.0f;
2589 for (
auto bucket =
static_cast<size_t>(threadIdx.x); bucket < BucketCount;
2590 bucket += blockDim.x) {
2591 auto const stored =
static_cast<uint16_t
>(__ldcs(&
registers[bucket]));
2593 stored == 0U ? 0U :
static_cast<uint32_t
>(stored >> Layout::mantissa_bits) + 1U
2595 histogram.Composite(bin, bins);
2597 local_restored +=
static_cast<float>(restore<Layout>(stored));
2601 auto const restored = block_reduce(
storage).Sum(local_restored);
2602 if (threadIdx.x == 0) {
2608template <
size_t BucketCount, hybr
id_variant Variant,
typename Layout = default_register_layout>
2611 __shared__ uint32_t bins[
nlz_bins];
2612 using block_histogram =
2613 cub::BlockHistogram<uint32_t, block_size, 1, nlz_bins, cub::BLOCK_HISTO_ATOMIC>;
2614 using block_reduce = cub::BlockReduce<float, block_size>;
2615 __shared__
typename block_reduce::TempStorage
storage;
2616 block_histogram histogram;
2617 histogram.InitHistogram(bins);
2618 float local_restored = 0.0f;
2621 for (
auto bucket =
static_cast<size_t>(threadIdx.x); bucket < BucketCount;
2622 bucket += blockDim.x) {
2623 auto const stored =
static_cast<uint16_t
>(__ldcs(&
registers[bucket]));
2625 stored == 0U ? 0U :
static_cast<uint32_t
>(stored >> Layout::mantissa_bits) + 1U
2627 histogram.Composite(bin, bins);
2629 local_restored +=
static_cast<float>(restore<Layout>(stored));
2633 auto const restored = block_reduce(
storage).Sum(local_restored);
2634 if (threadIdx.x == 0) {
2635 auto const estimates =
2638 *estimate = estimates.bbtools;
2640 *estimate = estimates.paper;
2648template <
size_t BucketCount,
typename Layout = default_register_layout>
2652 uint64_t*
const empty_out,
2653 double*
const estimates_out
2655 auto const row =
static_cast<size_t>(blockIdx.x);
2659 using block_reduce = cub::BlockReduce<cardinality_payload, block_size>;
2660 __shared__
typename block_reduce::TempStorage
storage;
2661 auto const*
const mine =
registers +
row * BucketCount;
2663 for (
auto bucket =
static_cast<size_t>(threadIdx.x); bucket < BucketCount;
2664 bucket += blockDim.x) {
2665 auto const stored =
static_cast<uint16_t
>(__ldcs(&mine[bucket]));
2669 local.restored +=
static_cast<float>(restore_midpoint<Layout>(stored));
2673 if (threadIdx.x == 0) {
2676 static_cast<float>(BucketCount),
static_cast<float>(
total.empty),
total.restored
2682template <
size_t BucketCount>
2686 uint16_t*
const scores_out
2688 auto const row =
static_cast<size_t>(blockIdx.x);
2692 auto const*
const mine =
registers +
row * BucketCount;
2693 auto*
const scores = scores_out +
row * BucketCount;
2694 for (
auto bucket =
static_cast<size_t>(threadIdx.x); bucket < BucketCount;
2695 bucket += blockDim.x) {
2696 scores[bucket] =
static_cast<uint16_t
>(__ldcs(&mine[bucket]));
__global__ void cardinality_kernel(uint32_t const *registers, uint64_t *empty_out, double *estimate_out)
Computes the cardinality of a single constructed sketch.
constexpr uint32_t row_words
auto const long_ranges_used
constexpr uint32_t max_block_words
uint32_t warp_grid_blocks(size_t warps) noexcept
Blocks for a grid-stride launch covering warps warps of block_size threads.
__global__ size_t query_row_offset
constexpr uint32_t index_match_cells_per_warp
Cells one warp owns per iteration of count_batch_index_matches_kernel: one per lane.
__global__ uint16_t const * sorted_keys
__global__ uint32_t reference_count
__device__ __forceinline__ void write_score_planes(uint32_t score, uint32_t group, uint32_t lane, uint32_t *planes)
Writes the bit-planes of one 32-bucket group whose scores the warp holds, one per lane.
_Pragma("unroll") for(uint32_t bit=0U
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const uint32_t uint32_t bool uint32_t uint32_t const uint32_t const * bitmap_starts
__global__ uint32_t const uint32_t const * pass_bits
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const uint32_t uint32_t bool uint32_t * candidate_bits
constexpr uint32_t match_filter_block_size
Threads per match_filter_kernel block.
__global__ uint32_t const uint32_t const uint32_t const uint32_t uint32_t size_t size_t word_end
__shared__ uint32_t packed_counts[]
constexpr bool match_filter_supported
Whether match_filter_kernel supports BucketCount: a lane caches at most two plane groups of each of i...
__host__ __device__ uint64_t batch_result_slot(uint32_t query_index, uint32_t reference_id, uint32_t query_id_offset, uint32_t reference_count) noexcept
Position of the pair (query_index of the tile, reference_id) in a tile's results: rows of reference_c...
__global__ void batch_scores_kernel(uint32_t const *const registers, uint32_t const row_count, uint16_t *const scores_out)
Copies the winning score of every register into compact row-major scores.
__global__ uint32_t uint32_t uint32_t * directory
constexpr uint32_t posting_range_block_size
Threads of the single index_posting_ranges_kernel block.
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const * ranges
__global__ uint32_t uint32_t * planes
__device__ __forceinline__ void load_plane_group(uint4 const *row, uint32_t group, uint32_t(&words)[score_planes])
Loads group group of one bit-plane row (score_plane_index layout).
static __global__ uint32_t const uint32_t uint32_t const uint32_t * match_counts
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const uint32_t minimum_matches
constexpr uint32_t cached_groups
__global__ uint32_t const * reference_planes
constexpr uint32_t bitmap_refine_waves
Cells per refine_batch_bitmap_kernel block that a tile of bitmap-selected pairs is split into when it...
auto const indexed_groups
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const uint32_t uint32_t bool uint32_t uint32_t const uint32_t const uint32_t const * bitmap_counts
__device__ void refine_write_result(uint32_t index, uint32_t query_index, uint32_t reference_id, uint32_t query_id_offset, uint32_t const *match_counts, uint32_t pair_id, pairwise_counts local, uint32_t lane, SearchResult *results, uint32_t *result_match_counts)
Warp-reduces one refined candidate and writes its stable result slot.
__global__ uint32_t uint32_t indexed_bucket_count
__device__ __forceinline__ void load_plane_chunk(uint4 const *row, uint32_t group, uint32_t chunk, uint32_t(&words)[4])
Loads plane chunk chunk (planes 4 chunk ... 4 chunk + 3) of group group of a bit-plane row (score_pla...
refine_candidates
Pairs refine_batch_bitmap_kernel compares.
@ reference_mask
Every pair of the layout whose reference is set in one reference-wide bitmap.
@ bitmap
Exactly the pairs set in a pass bitmap.
@ all
Every pair of the layout, or with pass bits requested, those meeting the threshold.
__global__ size_t uint32_t bool blacklist_view bool IncludeCardinality
__global__ size_t uint32_t bool blacklist_view blacklist
constexpr uint32_t all_pairs_block_size
Threads per refine_all_pairs_kernel block.
constexpr uint32_t all_pairs_word_parts
Parts a refine_all_pairs_kernel warp claims each 32-reference word in.
static __global__ uint32_t const uint16_t const uint32_t uint32_t uint16_t key_mask
__host__ __device__ float cardinality_f32(float bucket_count, float empty_count, float sum_restored) noexcept
Single-precision cardinality for the throughput-bound cardinality reduction.
__global__ uint16_t const uint32_t uint32_t uint32_t key_count
__device__ hybrid_cardinality_estimates hybrid_estimates_f32(uint32_t const *bins, float bucket_count, float restored_sum) noexcept
Single-precision hybrid_estimates for the latency-bound hybrid scan.
__device__ void classify(pairwise_counts &target, uint32_t this_score, uint32_t other_score) noexcept
Classifies one score relative to this into target.
__global__ void hybrid_cardinality_variant_kernel(uint32_t const *const registers, double *const estimate)
Computes one HybridDDL variant estimate for estimator comparisons.
__device__ uint2 lane_posting_range(uint32_t const *offsets, uint16_t const *sorted_keys, uint32_t reference_count, uint32_t bucket, uint32_t key, uint32_t key_count)
One thread's posting range for a bucket/key cell of a dense or sparse index.
static __global__ uint32_t const uint16_t const uint32_t uint32_t uint16_t uint32_t uint32_t * range_end_scan
constexpr uint32_t match_filter_planes
Low planes match_filter_kernel compares.
constexpr uint32_t block_size
Threads per CTA for the single-pair and cardinality reduction kernels.
constexpr uint32_t bitmap_refine_group
Queries one refine_batch_bitmap_kernel block refines together.
__global__ uint32_t uint32_t uint64_t uint32_t * ids
__global__ uint32_t const uint32_t const uint32_t const uint32_t uint32_t size_t word_begin
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t tile
constexpr uint32_t shared_construction_block_size
Threads per CTA for the CTA-local construction kernel.
constexpr uint32_t score_planes
Bits per score, and so bit-planes per 32-bucket group.
__global__ uint32_t uint32_t uint32_t const uint32_t uint32_t uint16_t uint32_t const SearchResult uint32_t * result_match_counts
__device__ __forceinline__ void compare_plane_group(uint32_t const (&q)[score_planes], uint32_t const (&r)[score_planes], uint32_t reference_any, uint32_t reference_low_any, uint16_t key_mask, uint32_t match_lanes, plane_counts &counts) noexcept
Adds one group's comparison of query planes q with reference planes r, whose nonzero buckets referenc...
__global__ void refine_all_pairs_kernel(uint32_t const *query_planes, uint32_t query_id_offset, uint32_t query_count, uint32_t const *reference_planes, uint32_t reference_count, SearchResult *results, uint32_t *cell_counter, uint32_t const *reference_mask)
Exactly compares the layout pairs selected by reference_mask, without match counts.
__global__ size_t input_size
__host__ __device__ constexpr uint32_t sparse_directory_shift(uint32_t reference_count) noexcept
Low key bits a sparse key directory cell spans for reference_count references.
constexpr uint32_t warps_per_block
__host__ __device__ double cardinality(double bucket_count, double empty_count, double sum_restored) noexcept
Public DDL cardinality estimate.
constexpr size_t bitmap_refine_dynamic_bytes
Shared bytes one refine_batch_bitmap_kernel block stages.
constexpr uint32_t index_tile_lane_postings
Longest posting range one count_batch_index_tile_kernel lane walks alone.
__global__ size_t uint32_t * registers
__global__ uint32_t const uint32_t const uint32_t const uint32_t first_query_id
__global__ uint32_t uint32_t first_word
__global__ uint32_t const uint32_t const uint32_t const uint32_t uint32_t size_t size_t uint32_t SearchResult * packed
static __global__ uint32_t const uint16_t const uint32_t uint32_t uint16_t uint32_t * range_begin
__device__ __forceinline__ refine_cells make_refine_cells(uint32_t query_id_offset, uint32_t query_count, uint32_t group, uint32_t words_per_query, uint32_t max_block_words, uint32_t waves)
Splits a tile into refine_cells, about waves cells per block when the tile allows,...
__host__ __device__ constexpr uint64_t hash_kmer(uint64_t packed_kmer) noexcept
Hashes a canonical packed k-mer into the value space used by DDL registers.
bool uses_tiled_index_counts(uint32_t reference_count, uint32_t query_count, uint32_t indexed_bucket_count) noexcept
Whether a batch counts through count_batch_index_tile_kernel.
constexpr uint32_t word_slices
__device__ __forceinline__ uint32_t plane_any(uint32_t const (&planes)[score_planes]) noexcept
Buckets of a plane group holding a nonzero score.
__global__ size_t uint32_t query_count
__global__ uint32_t const uint32_t uint32_t const uint32_t const SearchResult * results
constexpr size_t bitmap_refine_shared_bytes
Shared bytes refine_batch_bitmap_kernel may stage query planes in; one block per SM fits on every sup...
constexpr uint32_t wide_chunk_buckets
Buckets one contiguous chunk covers, for a query/reference score pair.
__global__ void hybrid_posting_ranges_kernel(uint16_t const *queries, uint32_t const *offsets, uint32_t const *bitmap_cells, uint32_t bitmap_count, uint32_t words, uint32_t indexed_buckets, uint16_t key_mask, uint2 *ranges, uint32_t *bitmap_starts, uint32_t *bitmap_counts)
Splits each query's posting ranges into short lists and bitmap offsets.
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const uint32_t uint32_t query_id_offset
__global__ uint32_t const uint32_t const uint32_t const uint32_t uint32_t size_t size_t uint32_t packed_base
constexpr size_t maximum_batch_rows
Stored sketches one batch launch may index, bounded by the CUDA grid dimension.
__device__ __forceinline__ uint32_t plane_low_any(uint32_t const (&planes)[score_planes]) noexcept
Buckets of a plane group whose score is nonzero below the top plane.
__host__ __device__ constexpr uint32_t sparse_directory_entries(uint32_t reference_count) noexcept
Bounds per bucket in a sparse key directory.
__shared__ uint32_t long_count
constexpr uint32_t groups_per_row
__global__ __launch_bounds__(shared_construction_block_size) void add_shared_kernel(uint64_t const *input
Constructs a sketch through a CTA-local shared-memory register array.
__shared__ uint32_t claimed_cell
__global__ uint16_t const uint32_t uint32_t uint32_t unsigned long long * work
__global__ size_t uint32_t bool vector_input
__device__ uint32_t sum_posting_bitmaps(uint32_t const *bitmaps, uint32_t const *starts, uint32_t count, uint32_t word, uint32_t minimum_matches, uint32_t passed, uint32_t(&counters)[CounterBits])
Sums one query's posting bitmaps over one word of references in bit-sliced counters.
__global__ uint32_t row_count
__device__ __forceinline__ void load_256_global_nc(const T *ptr, T *out)
Loads 256 bits from global memory using the non-coherent cache path.
__global__ uint32_t const uint32_t const uint32_t const uint32_t uint32_t size_t size_t uint32_t SearchResult uint32_t * packed_match_counts
auto const words_per_query
__global__ uint32_t uint32_t uint32_t const uint32_t uint32_t uint16_t uint32_t const SearchResult uint32_t uint32_t uint32_t uint32_t * cell_counter
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const uint32_t uint32_t bool all_to_all
__device__ __forceinline__ uint32_t next_cell(uint32_t *cell_counter, uint32_t cell, uint32_t &claimed)
The cell a refinement-style block processes after cell: claimed from cell_counter,...
__shared__ uint32_t next_word
__shared__ block_scan::TempStorage storage
__device__ uint2 directory_posting_range(uint16_t const *sorted_keys, uint32_t const *directory, uint32_t reference_count, uint32_t bucket, uint32_t sparse_key)
One sparse bucket/key posting range, narrowed through the bucket's key directory.
__global__ void match_filter_kernel(uint32_t const *query_planes, uint32_t query_id_offset, uint32_t query_count, uint32_t const *reference_planes, uint32_t reference_count, uint32_t indexed_bucket_count, uint32_t minimum_matches, uint32_t *candidate_bits, uint32_t *cell_counter)
Records in candidate_bits (candidate-bitmap layout) the pairs of the layout whose scores agree in the...
__device__ __forceinline__ void classify_query_words(pairwise_counts &target, uint32_t const (&q)[8], ReferenceScore const *reference) noexcept
Classifies 16 buckets of an already loaded 16-bit query chunk against one load_256_alignment aligned ...
constexpr uint32_t index_tile_long_capacity
Long posting ranges one count_batch_index_tile_kernel block walks together.
constexpr uint32_t bitmap_refine_block_size
Threads per refine_batch_bitmap_kernel block.
__shared__ uint4 query_plane_storage[]
static __global__ uint32_t const * offsets
for(auto i=static_cast< size_t >(blockIdx.x) *block_size+threadIdx.x;i< total;i+=static_cast< size_t >(gridDim.x) *block_size)
__host__ __device__ constexpr uint32_t score_plane_index(uint32_t group, uint32_t plane) noexcept
Position of plane plane of 32-bucket group group within a row's bit-planes.
__global__ uint32_t uint32_t uint32_t SearchResult uint32_t const * reference_mask
__global__ __maxnreg__(48) void count_batch_index_tile_kernel(QueryRow const *queries
Counts index matches for one query against one reference tile in shared memory.
constexpr size_t refine_block_row_bytes
Reference-row bytes one refine_batch_index_candidates_kernel block spans.
constexpr int32_t nlz_bins
NLZ histogram bins for the hybrid estimator, including the empty tier.
__global__ void hybrid_cardinality_kernel(uint32_t const *const registers, hybrid_cardinality_estimates *const estimates)
Computes BBTools and paper-style HybridDDL estimates in one register scan.
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t uint32_t uint16_t const uint2 const uint32_t uint32_t bool uint32_t uint32_t const * bitmaps
static __global__ uint32_t const uint32_t uint32_t const * postings
__global__ uint32_t const uint32_t uint32_t const uint32_t const * candidate_count
__shared__ uint2 long_ranges[index_tile_long_capacity]
constexpr uint32_t lane_groups
__global__ uint32_t const uint32_t const uint32_t const * word_offsets
__host__ __device__ uint16_t score(uint64_t hash) noexcept
Computes the 16-bit DDL rarity score for hash.
__global__ uint32_t const uint32_t uint32_t const * candidate_ids
__global__ void batch_cardinality_kernel(uint32_t const *const registers, uint32_t const row_count, uint64_t *const empty_out, double *const estimates_out)
Computes the cardinality of every stored sketch in one launch.
__global__ size_t uint16_t * rows
constexpr bool bitmap_refine_staged
Whether a query group's planes fit bitmap_refine_shared_bytes.
uint32_t index_tile_references(uint32_t reference_count, uint32_t query_count, uint32_t multiprocessors, uint32_t max_shared_bytes) noexcept
References per tile, bounded by the device's shared memory and small enough that the batch launches a...
@ counts
One 24-byte batch_search_result per pair: query ID, reference ID and the lower, equal,...
Exact membership within the candidate's destination bucket.
__device__ bool contains(uint64_t key, uint64_t hash, size_t bucket) const noexcept
hash must be hash_kmer(key); construction already holds it.
Per-thread accumulator feeding the CUB block reduction for cardinality.
friend __device__ cardinality_payload operator+(cardinality_payload left, cardinality_payload right) noexcept
Sums two accumulators for the block reduction.
Bit-sliced pairwise counts of one 32-bucket group, accumulated per lane.
uint32_t occupied
Buckets whose scores differ or whose reference score is nonzero: all but both-empty.
uint32_t differ
Buckets whose scores differ.
__device__ pairwise_counts reduce() const noexcept
Warp-reduces the counts into an exact pairwise summary.
Selects posting lists whose bitmap uses no more storage than their reference IDs.
__device__ bool operator()(uint32_t cell) const
uint16_t const * sorted_keys
How a refinement-style block splits a tile: cell c holds query group c % groups and the reference wor...
Per-thread accumulator feeding a CUB block reduction for fused summaries.
friend __device__ summary_payload operator+(summary_payload left, summary_payload const &right) noexcept
Sums two accumulators for CUB block reductions.
__device__ summary_payload & operator+=(summary_payload const &other) noexcept
Accumulates other into this instance for block reductions.
Popcount of one pass-bit word.
__host__ __device__ uint32_t operator()(uint32_t word) const noexcept
Experimental DDL cardinality estimates computed from one register scan.
Raw per-bucket classification counts for one sketch pair comparison.
uint32_t both_empty
Both registers are empty in that bucket.
uint32_t equal
this register equals the other's in that bucket.
uint32_t lower
this register is strictly lower than the other's in that bucket.
uint32_t higher
this register is strictly higher than the other's in that bucket.
Pairwise counts with an optional compile-time cardinality field.