51 bool packed_input =
false,
54 constexpr uint32_t tile_size = 256 * 8;
55 __shared__ uint32_t
cells[tile_size / 8 + 4];
56 __shared__ uint32_t
local[BucketCount];
57 for (uint32_t i = threadIdx.x; i < BucketCount; i += blockDim.x) {
60 uint32_t hash_ceiling = UINT32_MAX;
62 auto const mask = (uint64_t{1} << (2 * k)) - 1;
63 auto const valid_mask = (uint32_t{1} << k) - 1;
64 for (
size_t tile = first_tile;
tile < windows;
tile += tile_stride) {
65 auto const count = cuda::std::min(tile_size, windows -
static_cast<uint32_t
>(
tile));
66 if constexpr (Naive) {
70 auto const start = threadIdx.x * 8;
71 auto const*
const words =
reinterpret_cast<uint32_t const*
>(sequence);
72 for (uint32_t i = start; i < cuda::std::min(start + 8U,
count); ++i) {
73 uint64_t forward = 0, reverse = 0;
75 for (uint32_t j = 0; valid && j < k; ++j) {
76 auto const at =
tile + i + j;
80 code = (
words[at / 16] >> (2 * (at % 16))) & 3U;
82 switch (sequence[at]) {
103 forward = (forward << 2) | code;
104 reverse |= uint64_t{code ^ 2U} << (2 * j);
106 if (!valid)
continue;
107 auto const word = forward > reverse ? forward : reverse;
109 if (
static_cast<uint32_t
>(hash >> 32) > hash_ceiling)
continue;
110 auto const incoming = score<Layout>(hash);
111 auto const bucket = bucket_of<BucketCount>(hash);
112 if constexpr (HasBlacklist) {
115 atomicMax(&
local[bucket], incoming);
118 atomicMax(&
local[bucket], incoming);
123 auto const padded = (
count + 7) / 8 + 4;
128 auto const*
const words =
reinterpret_cast<uint32_t const*
>(sequence);
129 auto const total = (
static_cast<size_t>(windows) + k - 1 + 7) / 8;
130 for (uint32_t cell = threadIdx.x; cell < padded; cell += blockDim.x) {
131 auto const index =
tile / 8 + cell;
132 if (index >=
total) {
137 auto const reversed = __brev((
words[index / 2] >> (16 * (index % 2))) << 16);
138 cells[cell] = ((reversed & 0x5555U) << 1) | ((reversed >> 1) & 0x5555U);
141 for (uint32_t cell = threadIdx.x; !packed_input && cell < padded; cell += blockDim.x) {
142 auto const pos = cell * 8;
143 uint32_t ascii[2] = {};
144 if ((
reinterpret_cast<uintptr_t
>(sequence +
tile) & 7U) == 0 &&
145 pos + 7 <
count + k - 1) {
146 auto const value = *
reinterpret_cast<uint2 const*
>(sequence +
tile + pos);
151 for (uint32_t j = 0; j < 8; ++j) {
152 auto const byte = pos + j <
count + k - 1
153 ?
static_cast<uint8_t
>(sequence[
tile + pos + j])
155 ascii[j / 4] |=
static_cast<uint32_t
>(byte) << (8 * (j % 4));
158 uint32_t
packed = 0, bad = 0;
160 for (uint32_t j = 0; j < 2; ++j) {
162 auto const thymine = (ascii[j] & ~(ascii[j] << 1) & 0x04040404U) >> 2;
163 auto const normalised = (ascii[j] ^ (thymine * 0x11U)) & 0xD9D9D9D9U;
164 auto const bad_bytes = __vsetne4(normalised, 0x41414141U);
165 auto const codes = (ascii[j] >> 1) & 0x03030303U;
168 bad = __dp4a(bad_bytes, 0x08040201U << (4 * j), bad);
173 auto const start = threadIdx.x * 8;
175 auto const cell = start / 8;
176 auto const a =
cells[cell], b =
cells[cell + 1];
177 auto const c =
cells[cell + 2], d =
cells[cell + 3];
179 (uint64_t{a} << 48) | (uint64_t{b & 0xFFFFU} << 32) | (c << 16) | (d & 0xFFFFU);
181 auto bad = uint64_t{a >> 16} | (uint64_t{b >> 16} << 8) |
182 (uint64_t{c >> 16} << 16) | (uint64_t{d >> 16} << 24);
184 auto const e =
cells[cell + 4];
185 low = uint64_t{e & 0xFFFFU} << 48;
186 bad |= uint64_t{e >> 16} << 32;
189 auto reverse_word = [](uint64_t
word) {
190 auto const bits = cuda::std::bit_reverse(
word);
191 return (((
bits & 0xAAAAAAAAAAAAAAAAULL) >> 1) |
192 ((
bits & 0x5555555555555555ULL) << 1)) ^
193 0xAAAAAAAAAAAAAAAAULL;
195 auto reverse_high = reverse_word(
high);
196 auto reverse_low = reverse_word(
low);
198 auto const end = (bad & valid_mask & ~uint64_t{0x7F}) == 0
199 ? cuda::std::min(8U,
count - start)
203 auto slide = [&]<
bool Long>() {
205 for (uint32_t i = 0; i < end; ++i) {
206 if ((bad & valid_mask) == 0) {
207 auto const forward =
high >> (64 - 2 * k);
208 auto const reverse = reverse_high & mask;
209 auto const word = forward > reverse ? forward : reverse;
211 if (
static_cast<uint32_t
>(hash >> 32) <= hash_ceiling) {
212 auto const incoming = score<Layout>(hash);
213 auto const bucket = bucket_of<BucketCount>(hash);
215 if constexpr (HasBlacklist) {
218 atomicMax(&
local[bucket], incoming);
221 atomicMax(&
local[bucket], incoming);
226 if constexpr (Long) {
229 reverse_high = (reverse_high >> 2) | (reverse_low << 62);
239 slide.template operator()<
true>();
241 slide.template operator()<
false>();
247 if ((++
tiles == 4 ||
tiles % 16 == 0) &&
tile + tile_stride < windows) {
248 using reduce = cub::BlockReduce<uint32_t, 256>;
249 __shared__
typename reduce::TempStorage exchange_scratch;
250 __shared__ uint32_t exchange_ceiling;
251 uint32_t minimum = 0xffffU;
253 for (uint32_t b = threadIdx.x; b < BucketCount; b += blockDim.x) {
254 auto const value =
local[b];
256 cuda::atomic_ref<uint32_t, cuda::thread_scope_device>{
registers[b]}.load(
257 cuda::memory_order_relaxed
259 if (value > global) global = cuda::std::max(value, atomicMax(&
registers[b], value));
261 minimum = cuda::std::min(minimum, global);
263 auto const result = reduce(exchange_scratch).Reduce(minimum, cuda::minimum<>{});
264 if (threadIdx.x == 0) {
267 result == 0 ? UINT32_MAX : uint32_t(restore<Layout>(result) >> 32);
270 hash_ceiling = exchange_ceiling;
274 for (uint32_t i = threadIdx.x; i < BucketCount; i += blockDim.x) {
379 cuda::stream_ref stream,
380 cuda::std::optional<blacklist_view>
blacklist = cuda::std::nullopt
382 if (k < 1 || k > 31) {
385 size_t const size = sequence.size();
389 size_t const windows_size = size - k + 1;
390 if (windows_size > std::numeric_limits<uint32_t>::max()) {
393 auto const windows =
static_cast<uint32_t
>(windows_size);
396 auto const kernel = add_sequence_single_kernel<BucketCount, Layout, Filter>;
397 auto const multiprocessors =
398 stream.device().attribute(cuda::device_attributes::multiprocessor_count);
400 if constexpr (Filter) {
402 auto const carveout = cudaFuncSetAttribute(
403 kernel, cudaFuncAttributePreferredSharedMemoryCarveout, 50
405 if (carveout != cudaSuccess)
return carveout;
407 int blocks_per_sm = 0;
408 auto const occupancy =
409 cudaOccupancyMaxActiveBlocksPerMultiprocessor(&blocks_per_sm, kernel, 256, 0);
410 if (occupancy != cudaSuccess)
return occupancy;
411 size_t const need = (
static_cast<size_t>(windows) + 2047) / 2048;
412 size_t const capacity =
static_cast<size_t>(multiprocessors) * blocks_per_sm;
413 size_t const blocks_size =
414 capacity == 0 ?
size_t{1} : (capacity < need ? capacity : need);
415 auto const blocks =
static_cast<uint32_t
>(blocks_size);
416 kernel<<<blocks, 256, 0, stream.get()>>>(
419 return cudaGetLastError();
422 return blacklist ? launch.template operator()<
true>() : launch.template operator()<
false>();