cuDDL
Loading...
Searching...
No Matches
Namespaces | Classes | Typedefs | Enumerations | Functions
cuddl Namespace Reference

Batch operations over contiguous device rows. More...

Namespaces

namespace  a48
 
namespace  detail
 

Classes

struct  all_to_all_shard
 Part cuDDL of count disjoint parts of an all-to-all search. More...
 
class  all_to_all_tile_queue
 Thread-safe source of the all-to-all tiles one process searches. More...
 
struct  batch_result_tile
 Non-owning view of one batch-search tile's results. More...
 
struct  batch_search_requirements
 Caller-owned storage requirements for one bounded query tile. More...
 
struct  batch_search_result
 Stable query/reference IDs and their exact query-relative bucket comparison counts. More...
 
struct  CudaError
 CUDA failure payload: status code, call site, and formatted message. More...
 
class  device_blacklist
 Reusable, move-only device lookup. More...
 
struct  device_span
 A span that is assumed to point to device-accessible memory. More...
 
struct  Error
 Error payload carried in Result on failure. More...
 
struct  genome_slice
 One rank's share of a genome collection split across an MPI communicator. More...
 
class  host_batch_results
 Host copy of a batch_result_tile holding only the passing results, packed in query-major, reference order, in pinned memory; tile views it. More...
 
struct  hybrid_cardinality_estimates
 Experimental DDL cardinality estimates computed from one register scan. More...
 
struct  InvalidArgumentError
 Invalid-argument failure payload. More...
 
class  kmer_blacklist
 Immutable canonical k-mers excluded from construction, independent of sketch size. More...
 
struct  packed_pairwise_counts
 One pair's exact bucket comparison counts in 64 bits, the storage and transfer form of batch-search results. More...
 
struct  pairwise_counts
 Raw per-bucket classification counts for one sketch pair comparison. More...
 
struct  pairwise_summary
 Pairwise counts with an optional compile-time cardinality field. More...
 
struct  path_build_options
 Knobs for a build the caller feeds with FASTA/FASTQ paths. More...
 
class  query_sketch_batch
 Query sketches held on the device, in the shape a search consumes them. More...
 
struct  reference_build_statistics
 How a build moved sequence bytes to the device. More...
 
class  reference_database
 Move-only owner of one immutable contiguous reference database. More...
 
class  reference_database_file
 Host-owned reference sketches and labels, ready for binary storage or GPU upload. More...
 
struct  reference_database_metadata
 Recorded metadata for an immutable reference database. More...
 
class  reference_index
 Move-only acceleration data for an immutable reference database. More...
 
class  reference_index_file
 Index-file persistence and the validation boundary for reference_index. More...
 
struct  reference_search_result
 Stable reference ID and exact query-relative bucket comparison counts. More...
 
struct  register_layout
 Compile-time split of a 16-bit DDL score into exponent and mantissa fields. More...
 
struct  replica_search
 How a replicated_database search splits and delivers its tiles. More...
 
struct  replica_tile
 One all-to-all tile as a replicated_database search delivers it. More...
 
class  replicated_database
 One reference database replicated on explicitly chosen devices, searched all-to-all by all of them at once. More...
 
struct  ResourceError
 Resource failure payload: allocation, I/O, or capacity errors. More...
 
struct  Result
 Fallible API result: cuda::std::expected<T, Error> with cuDDL factories. More...
 
struct  Result< void >
 
class  result_file
 Writes search results to files while the GPUs search on. More...
 
struct  result_file_options
 
struct  result_file_statistics
 What a result_file wrote and how long its callers waited for it. More...
 
struct  score_compatibility
 Construction parameters of compatible score rows. More...
 
struct  search_options
 Matching-bucket threshold, independent of whether an index accelerates the search. More...
 
struct  sequence_build_options
 Knobs for a build the caller feeds with bases it already holds. More...
 
class  shared_tile_counter
 A tile counter shared by every rank of a communicator, for replica_search::next_tile. More...
 
struct  similarity_record
 One pair of a thresholded search in result_format::similarity. More...
 
class  sketch
 Move-only owning DDL sketch backed by one contiguous device allocation. More...
 
struct  SourceLocation
 File, line, and column for a C++ call site or FASTX input position. More...
 
class  top_k_search
 Keeps each genome's k most similar genomes from search tiles on the device. More...
 

Typedefs

using default_register_layout = register_layout< 6, 10 >
 
using fasta_parse_result = detail::fasta_parse_result
 Result of parsing one FASTA/FASTQ file into packed canonical k-mers.
 
using transfer_mode = detail::transfer_mode
 How a build gets its sequence bytes to the device.
 
using decompression_backend = detail::decompression_backend
 Selects decompression independently of host transfers.
 
using sequence_record = detail::sequence_record
 One record of a genome the caller already holds: bases only.
 
using sequence_genome = detail::sequence_genome
 One genome as its records, in order. No records keeps the genome's ID with a zero row.
 

Enumerations

enum class  ErrorCategory : uint8_t { cuda , invalid_argument , resource }
 Error category for Result failures. More...
 
enum class  index_storage { automatic , dense , sparse }
 Dense offsets favor query latency; sparse sorted keys reduce index memory and build time. More...
 
enum class  result_format : uint8_t { counts , similarity }
 How a result_file stores each pair. More...
 

Functions

template<size_t BucketCount>
Result< void > compare_batch_async (device_span< uint32_t const > left_rows, device_span< uint32_t const > right_rows, device_span< pairwise_summary > outputs, cuda::stream_ref stream)
 Compares corresponding rows from two contiguous register batches.
 
template<size_t BucketCount, typename Layout = default_register_layout>
Result< void > cardinality_batch_async (device_span< uint32_t const > sketches, device_span< uint64_t > empty_out, device_span< double > estimates_out, cuda::stream_ref stream)
 Computes the cardinality of every stored sketch.
 
template<size_t BucketCount>
Result< void > extract_scores_batch_async (device_span< uint32_t const > sketches, device_span< uint16_t > scores_out, cuda::stream_ref stream)
 Copies the winning score of every register into compact row-major scores.
 
Result< genome_slice > slice_genomes (std::filesystem::path const &input, MPI_Comm comm, unsigned workers=default_parser_workers())
 Lists a genome collection once and splits it into one contiguous slice per rank of comm, of about equal file bytes.
 
Result< reference_database_file > gather_sketches (reference_database_file const &mine, genome_slice const &slice, MPI_Comm comm)
 Gathers every rank's sketches of its genome_slice into one file on every rank.
 
template<uint32_t K, size_t BucketCount, typename Layout , typename OnTile >
Result< void > search_all_to_all (replicated_database< K, BucketCount, Layout > &group, OnTile &&on_tile, MPI_Comm comm)
 Compares every pair of group's references, sharing the tiles among the ranks of comm; collective.
 
template<uint32_t K, size_t BucketCount, typename Layout , typename OnTile >
Result< void > search_all_to_all (replicated_database< K, BucketCount, Layout > &group, OnTile &&on_tile, search_options options, MPI_Comm comm)
 The threshold form of search_all_to_all: only pairs passing options reach on_tile.
 
void cuda_abort_on_error (cudaError_t error, std::source_location location=std::source_location::current())
 Terminates the process when error is not cudaSuccess (for RAII teardown only).
 
cuda::std::unexpected< Error > Err (Error error)
 Failure return; converts to any Result<T> via cuda::std::unexpected.
 
Result< void > Ok () noexcept
 Success return for Result<void>; same as return {}.
 
Result< void > cuda_try (cudaError_t error, std::source_location location=std::source_location::current())
 Checks a CUDA runtime call and returns an error on failure.
 
template<std::invocable Operation>
auto cuda_try (Operation &&operation, std::source_location location=std::source_location::current())
 Converts CCCL exceptions to the same Result errors as CUDA status-returning APIs.
 
Result< fasta_parse_result > parse_fasta_file (std::string const &path, uint32_t k, unsigned threads=std::thread::hardware_concurrency())
 Parses a FASTA/FASTQ file's sequence bases into packed canonical k-mers of length k.
 
__host__ __device__ constexpr float weighted_kmer_identity (pairwise_counts const &counts) noexcept
 Weighted k-mer identity of a pair, equal / max(equal + min(lower, higher), 6), as sketch::wkid computes it, or 0 when no bucket is comparable.
 
Result< void > download (batch_result_tile const &tile, cuda::stream_ref stream, host_batch_results &host)
 Copies a device tile's passing results into host's pinned buffers, growing them only when needed, and synchronizes stream.
 
Result< host_batch_results > download (batch_result_tile const &tile, cuda::stream_ref stream)
 Copies a device tile's passing results to new pinned host memory; see the overload that downloads into reusable host_batch_results.
 
template<typename F >
Result< void > for_each_passing (batch_result_tile const &tile, cuda::stream_ref stream, F &&f)
 Calls f on the host with every passing result of a device tile, query-major and in reference order; synchronizes stream.
 
unsigned default_parser_workers ()
 Loaders a path build runs unless the caller asks for another count.
 
Result< std::vector< std::filesystem::path > > genome_paths (std::filesystem::path const &directory)
 The FASTA/FASTQ files under directory, searched recursively and sorted by path, which is the order that numbers reference IDs.
 
Result< std::vector< std::filesystem::path > > list_genomes (std::filesystem::path const &input)
 Paths from a directory (see genome_paths) or from a text file with one path per line, in the file's order.
 
template<uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
Result< cuda::device_buffer< uint32_t > > build_sketch_store (std::span< std::filesystem::path const > paths, cuda::stream_ref stream, path_build_options options={})
 Sketches one genome per plain or gzip/BGZF FASTA/FASTQ file into a device store.
 
score_compatibility decoded_compatibility (uint8_t k, uint16_t buckets, uint8_t exponent_bits, uint64_t seed)
 Builds a cuDDL score-compatibility object describing a decoded BBTools A48 database.
 
std::vector< batch_search_result > select_top_k (std::vector< batch_search_result > rows, uint32_t k)
 The k best rows of each query among rows, as top_k_search ranks them, best first and query by query.
 

Detailed Description

Batch operations over contiguous device rows.

Every function takes flat row-major spans of BucketCount uint32_t registers per sketch, the layout of a single sketch allocation and of the stores the file builders write, and runs the whole batch in one kernel launch, so a collection of N rows costs one launch instead of N.

Inputs and outputs must remain valid until stream completes.

Typedef Documentation

◆ decompression_backend

Selects decompression independently of host transfers.

Definition at line 155 of file reference_database_file.cuh.

◆ default_register_layout

Definition at line 22 of file register.cuh.

◆ fasta_parse_result

Result of parsing one FASTA/FASTQ file into packed canonical k-mers.

Definition at line 16 of file fastx.hpp.

◆ sequence_genome

One genome as its records, in order. No records keeps the genome's ID with a zero row.

Definition at line 161 of file reference_database_file.cuh.

◆ sequence_record

One record of a genome the caller already holds: bases only.

Definition at line 158 of file reference_database_file.cuh.

◆ transfer_mode

How a build gets its sequence bytes to the device.

Definition at line 152 of file reference_database_file.cuh.

Enumeration Type Documentation

◆ ErrorCategory

enum class cuddl::ErrorCategory : uint8_t
strong

Error category for Result failures.

Enumerator
cuda 
invalid_argument 
resource 

Definition at line 57 of file error.hpp.

◆ index_storage

enum class cuddl::index_storage
strong

Dense offsets favor query latency; sparse sorted keys reduce index memory and build time.

Automatic picks whichever needs fewer device bytes for the database's reference count.

Enumerator
automatic 
dense 
sparse 

Definition at line 60 of file reference_database.cuh.

◆ result_format

enum class cuddl::result_format : uint8_t
strong

How a result_file stores each pair.

Enumerator
counts 

One 24-byte batch_search_result per pair: query ID, reference ID and the lower, equal, higher and both-empty bucket counts, six little-endian uint32.

similarity 

The pair's weighted_kmer_identity as a 4-byte float, computed on the GPU.

A tile that holds every pair is written as its first query ID and query count (two uint32) followed by one float per pair in the tile's order: query-major, each query against every later reference in an all-to-all search and against every reference otherwise. Every other pair is a 12-byte similarity_record.

Definition at line 35 of file result_file.cuh.

35 : uint8_t {
38 counts,
45};
@ counts
One 24-byte batch_search_result per pair: query ID, reference ID and the lower, equal,...
@ similarity
The pair's weighted_kmer_identity as a 4-byte float, computed on the GPU.

Function Documentation

◆ build_sketch_store()

template<uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
Result< cuda::device_buffer< uint32_t > > cuddl::build_sketch_store ( std::span< std::filesystem::path const >  paths,
cuda::stream_ref  stream,
path_build_options  options = {} 
)

Sketches one genome per plain or gzip/BGZF FASTA/FASTQ file into a device store.

Each genome gets BucketCount registers, the row layout the batch operations in batch.cuh consume, in the order of paths. The rows never leave the device. The build is synchronous.

Definition at line 685 of file reference_database_file.cuh.

688 {}
689) try {
690 return [&]() -> Result<cuda::device_buffer<uint32_t>> {
691 if (options.blacklist) CUDDL_TRY(options.blacklist->get().validate(K, BucketCount, stream));
692
693 auto store = cuda::make_device_buffer<uint32_t>(
694 stream, stream.device(), paths.size() * BucketCount, cuda::no_init
695 );
696
697 CUDDL_TRY((detail::stage_paths<K, BucketCount, Layout>(
698 paths,
699 stream,
700 options.staging_bytes,
701 options.parser_workers,
702 options.transfer,
703 options.decompression,
704 options.direct_reads,
705 options.statistics,
706 [&](device_span<uint32_t const> group, size_t base, size_t count) -> Result<void> {
707 CUDDL_CUDA_TRY(
708 cuda::copy_bytes(
709 stream,
710 group,
711 device_span<uint32_t>{
712 store.data() + base * BucketCount, count * BucketCount
713 }
714 )
715 );
716 return Ok();
717 },
718 options.blacklist ? options.blacklist->get().view() : cuda::std::nullopt
719 )));
720 return store;
721 }();
722} catch (cuda::cuda_error const& error) {
723 return Err(Error::cuda(static_cast<cudaError_t>(error.status())));
724} catch (std::system_error const& error) {
725 return Err(Error::resource(error.what()));
726} catch (std::bad_alloc const& error) {
727 return Err(Error::resource(error.what()));
728}
#define CUDDL_TRY(expr)
Propagates a cuddl::Result failure from the enclosing function (GNU statement expression).
Definition error.hpp:225
cuda::std::unexpected< Error > Err(Error error)
Failure return; converts to any Result<T> via cuda::std::unexpected.
Definition error.hpp:176
transfer_mode transfer
How the build moves bytes.
std::optional< size_t > staging_bytes
Arena bytes.
unsigned parser_workers
Loaders to run.
std::optional< std::reference_wrapper< device_blacklist const > > blacklist
Reusable lookup; keep alive through this synchronous build.
reference_build_statistics * statistics
bool direct_reads
Read the files with O_DIRECT, past the page cache.

◆ cardinality_batch_async()

template<size_t BucketCount, typename Layout = default_register_layout>
Result< void > cuddl::cardinality_batch_async ( device_span< uint32_t const >  sketches,
device_span< uint64_t >  empty_out,
device_span< double >  estimates_out,
cuda::stream_ref  stream 
)
inline

Computes the cardinality of every stored sketch.

empty_out receives the empty-register count per row and estimates_out the cardinality estimate per row.

Definition at line 80 of file batch.cuh.

85 {
86 static_assert(BucketCount >= (size_t{1} << 11) && BucketCount <= (size_t{1} << 13));
87 static_assert((BucketCount & (BucketCount - 1U)) == 0U);
88
89 if (sketches.size() % BucketCount != 0U) {
90 return Err(Error::invalid_argument("sketch store must contain whole sketches"));
91 }
92 auto const row_count = sketches.size() / BucketCount;
93 if (empty_out.size() < row_count || estimates_out.size() < row_count) {
94 return Err(Error::invalid_argument("sketch store outputs are too small"));
95 }
96 if (row_count == 0U) {
97 return Ok();
98 }
99 if (sketches.data() == nullptr || empty_out.data() == nullptr ||
100 estimates_out.data() == nullptr) {
101 return Err(Error::invalid_argument("nonempty sketch store buffers must not be null"));
102 }
103 if (row_count > detail::maximum_batch_rows) {
104 return Err(Error::invalid_argument("sketch store has too many rows for one launch"));
105 }
106
107 detail::batch_cardinality_kernel<BucketCount, Layout>
108 <<<static_cast<uint32_t>(row_count), detail::block_size, 0, stream.get()>>>(
109 sketches.data(),
110 static_cast<uint32_t>(row_count),
111 empty_out.data(),
112 estimates_out.data()
113 );
114 return cuda_try(cudaGetLastError());
115}
__global__ uint32_t row_count
Definition kernels.cuh:1358
Result< void > Ok() noexcept
Success return for Result<void>; same as return {}.
Definition error.hpp:181
Result< void > cuda_try(cudaError_t error, std::source_location location=std::source_location::current())
Checks a CUDA runtime call and returns an error on failure.
Definition error.hpp:187
Here is the call graph for this function:

◆ compare_batch_async()

template<size_t BucketCount>
Result< void > cuddl::compare_batch_async ( device_span< uint32_t const >  left_rows,
device_span< uint32_t const >  right_rows,
device_span< pairwise_summary >  outputs,
cuda::stream_ref  stream 
)

Compares corresponding rows from two contiguous register batches.

The input spans are row-major and must contain the same whole number of BucketCount rows. outputs must hold at least one summary per row pair. Inputs and outputs must remain valid until stream completes. The whole batch runs in one kernel launch.

Definition at line 35 of file batch.cuh.

40 {
41 static_assert(BucketCount >= (size_t{1} << 11) && BucketCount <= (size_t{1} << 13));
42 static_assert((BucketCount & (BucketCount - 1U)) == 0U);
43
44 if (left_rows.size() != right_rows.size()) {
45 return Err(Error::invalid_argument("batch register spans must have equal sizes"));
46 }
47 if (left_rows.size() % BucketCount != 0U) {
48 return Err(Error::invalid_argument("batch register spans must contain whole rows"));
49 }
50 auto const pair_count = left_rows.size() / BucketCount;
51 if (outputs.size() < pair_count) {
52 return Err(Error::invalid_argument("batch output span is too small"));
53 }
54 if (pair_count != 0U && (left_rows.data() == nullptr || right_rows.data() == nullptr ||
55 outputs.data() == nullptr)) {
56 return Err(Error::invalid_argument("nonempty batch buffers must not be null"));
57 }
58 if (pair_count == 0U) {
59 return Ok();
60 }
61
62 constexpr size_t warps_per_block = detail::block_size / 32U;
63 constexpr size_t maximum_blocks = 65535U;
64 auto const blocks =
65 std::min((pair_count + warps_per_block - 1U) / warps_per_block, maximum_blocks);
66 detail::batch_summary_kernel<BucketCount>
67 <<<static_cast<uint32_t>(blocks), detail::block_size, 0, stream.get()>>>(
68 left_rows.data(), right_rows.data(), pair_count, outputs.data()
69 );
70 return cuda_try(cudaGetLastError());
71}
constexpr uint32_t warps_per_block
Definition kernels.cuh:935
Here is the call graph for this function:

◆ cuda_abort_on_error()

void cuddl::cuda_abort_on_error ( cudaError_t  error,
std::source_location  location = std::source_location::current() 
)
inline

Terminates the process when error is not cudaSuccess (for RAII teardown only).

Definition at line 144 of file error.hpp.

147 {
148 if (error == cudaSuccess) {
149 return;
150 }
151 const Error err = Error::cuda(error, location);
152 std::fputs(err.message().c_str(), stderr);
153 std::fputc('\n', stderr);
154 std::abort();
155}
Here is the call graph for this function:

◆ cuda_try() [1/2]

Result< void > cuddl::cuda_try ( cudaError_t  error,
std::source_location  location = std::source_location::current() 
)
inline

Checks a CUDA runtime call and returns an error on failure.

Definition at line 187 of file error.hpp.

187 {
188 if (error == cudaSuccess) {
189 return Ok();
190 }
191 return Err(Error::cuda(error, location));
192}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuda_try() [2/2]

template<std::invocable Operation>
auto cuddl::cuda_try ( Operation &&  operation,
std::source_location  location = std::source_location::current() 
)

Converts CCCL exceptions to the same Result errors as CUDA status-returning APIs.

Definition at line 197 of file error.hpp.

197 {
198 using Value = std::invoke_result_t<Operation>;
200 try {
201 if constexpr (std::is_void_v<Value>) {
202 std::forward<Operation>(operation)();
203 return Return::ok();
204 } else if constexpr (std::is_same_v<Value, cudaError_t>) {
205 return cuda_try(std::forward<Operation>(operation)(), location);
206 } else {
207 return Return::ok(std::forward<Operation>(operation)());
208 }
209 } catch (cuda::cuda_error const& error) {
210 return Return::err(Error::cuda(static_cast<cudaError_t>(error.status()), location));
211 } catch (std::bad_alloc const& error) {
212 return Return::err(Error::resource(error.what()));
213 }
214}
Fallible API result: cuda::std::expected<T, Error> with cuDDL factories.
Definition error.hpp:118
Here is the call graph for this function:

◆ decoded_compatibility()

score_compatibility cuddl::decoded_compatibility ( uint8_t  k,
uint16_t  buckets,
uint8_t  exponent_bits,
uint64_t  seed 
)
inline

Builds a cuDDL score-compatibility object describing a decoded BBTools A48 database.

Decoded rows come from the BBTools encoder/hash pipeline, not the cuDDL one. The encoder and hash identity fields therefore carry nonzero sentinels (2) distinct from cuDDL's own construction constants, while the mantissa/exponent split and bucket/key masks are taken from the asset's header so decoded rows pass cuDDL's normal validation paths.

Definition at line 15 of file refseq_parity.hpp.

15 {
17 compat.kmer_length = k;
18 compat.bucket_count = buckets;
19 compat.indexed_bucket_count = buckets;
20 compat.score_encoder_identity = 2U;
21 compat.exponent_bits = exponent_bits;
22 compat.mantissa_bits = 16U - exponent_bits;
23 compat.hash_identity = 2U;
24 compat.hash_seed = seed;
25 compat.canonicalisation_policy = 1U;
26 compat.blacklist_identity = 0U;
27 compat.blacklist_version = 0U;
28 compat.key_mask = 0xffffU;
29 return compat;
30}
Construction parameters of compatible score rows.

◆ default_parser_workers()

unsigned cuddl::default_parser_workers ( )
inline

Loaders a path build runs unless the caller asks for another count.

Machine thread count, falling back to 8 when the count is unavailable.

Definition at line 166 of file reference_database_file.cuh.

166 {
167 auto const hardware = std::thread::hardware_concurrency();
168 return hardware == 0U ? 8U : hardware;
169}

◆ download() [1/2]

Result< host_batch_results > cuddl::download ( batch_result_tile const &  tile,
cuda::stream_ref  stream 
)
inline

Copies a device tile's passing results to new pinned host memory; see the overload that downloads into reusable host_batch_results.

Definition at line 583 of file reference_database.cuh.

583 {
584 host_batch_results host(stream);
585 CUDDL_TRY(download(tile, stream, host));
586 return Result<host_batch_results>::ok(std::move(host));
587}
Host copy of a batch_result_tile holding only the passing results, packed in query-major,...
Result< void > download(batch_result_tile const &tile, cuda::stream_ref stream, host_batch_results &host)
Copies a device tile's passing results into host's pinned buffers, growing them only when needed,...
Here is the call graph for this function:

◆ download() [2/2]

Result< void > cuddl::download ( batch_result_tile const &  tile,
cuda::stream_ref  stream,
host_batch_results &  host 
)
inline

Copies a device tile's passing results into host's pinned buffers, growing them only when needed, and synchronizes stream.

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.

Definition at line 427 of file reference_database.cuh.

427 {
428 detail::device_scope const scope{stream};
429 host.layout_ = tile;
430 host.layout_.word_offsets = nullptr;
431 host.has_match_counts_ = tile.match_counts != nullptr;
432 if (tile.pass_bits == nullptr) {
433 auto const slots = static_cast<size_t>(tile.slot_count());
434 host.count_ = slots;
435 host.reserve(host.results_, slots, stream);
436 if (slots != 0U) {
438 cuda::copy_bytes(
439 stream,
440 cuda::std::span{tile.results, slots},
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,
451 cuda::std::span{tile.match_counts, slots},
452 cuda::std::span{host.match_counts_.data(), slots}
453 )
454 );
455 }
456 }
457 CUDDL_CUDA_TRY(stream.sync());
458 return Ok();
459 }
460
461 auto const words = static_cast<size_t>(tile.query_count) * tile.words_per_query();
462 host.reserve(host.pass_bits_, words, stream);
463 host.reserve(host.word_offsets_, words, stream);
464 if (words == 0U) {
465 host.count_ = 0U;
466 CUDDL_CUDA_TRY(stream.sync());
467 return Ok();
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 );
481 auto scan_storage = CUDDL_CUDA_TRY(
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,
497 cuda::std::span{tile.pass_bits, words},
498 cuda::std::span{host.pass_bits_.data(), words}
499 )
500 );
501 CUDDL_CUDA_TRY(stream.sync());
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) {
508 CUDDL_CUDA_TRY(stream.sync());
509 return Ok();
510 }
511 // Passing records are gathered into bounded device staging, so each chunk crosses the bus as
512 // one bulk copy rather than as scattered small writes.
513 constexpr size_t staging_records = size_t{1} << 23U;
514 auto const staged = std::min(count, staging_records);
515 auto staging = CUDDL_CUDA_TRY(
516 cuda::make_device_buffer<packed_pairwise_counts>(
517 stream, stream.device(), staged, cuda::no_init
518 )
519 );
520 auto staging_matches = CUDDL_CUDA_TRY(
521 cuda::make_device_buffer<uint32_t>(
522 stream, stream.device(), tile.match_counts == nullptr ? 0U : staged, cuda::no_init
523 )
524 );
525 auto const kernel = tile.upper_triangle
526 ? detail::gather_passing_kernel<true, packed_pairwise_counts>
527 : detail::gather_passing_kernel<false, packed_pairwise_counts>;
528 auto const* offsets = host.word_offsets_.data();
529 for (size_t first_word = 0U; first_word < words;) {
530 auto const base = offsets[first_word];
531 // The last word whose results all fit the staging buffer ends the chunk.
532 auto const last_word = static_cast<size_t>(
533 std::upper_bound(offsets + first_word + 1U, offsets + words, base + staged - 32U) -
534 offsets
535 );
536 auto const end = last_word == words ? count : static_cast<size_t>(offsets[last_word]);
537 kernel<<<
538 detail::warp_grid_blocks(last_word - first_word),
539 detail::block_size,
540 0,
541 stream.get()>>>(
542 tile.results,
543 tile.match_counts,
544 tile.pass_bits,
545 host.word_offsets_.data(),
546 tile.first_query_id,
547 tile.reference_count,
549 last_word,
550 base,
551 staging.data(),
552 tile.match_counts == nullptr ? nullptr : staging_matches.data()
553 );
554 CUDDL_CUDA_TRY(cudaGetLastError());
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 );
564 if (tile.match_counts != nullptr) {
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 }
574 first_word = last_word;
575 }
576 CUDDL_CUDA_TRY(stream.sync());
577 return Ok();
578}
#define CUDDL_CUDA_TRY(...)
Propagates a CUDA error wrapped in cuddl::Result<void>.
Definition error.hpp:248
auto const count
Definition kernels.cuh:1031
@ host
Any node: for buffers host threads fill.
__global__ size_t uint32_t const uint32_t const uint32_t uint32_t uint16_t uint32_t tile
Definition kernels.cuh:922
__global__ top_k_slots slots
Definition top_k.cuh:195
__global__ uint32_t uint32_t first_word
Definition top_k.cuh:234
constexpr bool staged
Definition kernels.cuh:1860
auto const words
Definition top_k.cuh:204
::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
Definition kernels.cuh:536
Here is the caller graph for this function:

◆ Err()

cuda::std::unexpected< Error > cuddl::Err ( Error  error)
inline

Failure return; converts to any Result<T> via cuda::std::unexpected.

Definition at line 176 of file error.hpp.

176 {
177 return cuda::std::unexpected<Error>(std::move(error));
178}

◆ extract_scores_batch_async()

template<size_t BucketCount>
Result< void > cuddl::extract_scores_batch_async ( device_span< uint32_t const >  sketches,
device_span< uint16_t >  scores_out,
cuda::stream_ref  stream 
)
inline

Copies the winning score of every register into compact row-major scores.

scores_out holds one uint16_t-per-register row per stored sketch.

Definition at line 123 of file batch.cuh.

127 {
128 static_assert(BucketCount >= (size_t{1} << 11) && BucketCount <= (size_t{1} << 13));
129 static_assert((BucketCount & (BucketCount - 1U)) == 0U);
130
131 if (sketches.size() % BucketCount != 0U) {
132 return Err(Error::invalid_argument("sketch store must contain whole sketches"));
133 }
134 auto const row_count = sketches.size() / BucketCount;
135 if (scores_out.size() < row_count * BucketCount) {
136 return Err(Error::invalid_argument("sketch store outputs are too small"));
137 }
138 if (row_count == 0U) {
139 return Ok();
140 }
141 if (sketches.data() == nullptr || scores_out.data() == nullptr) {
142 return Err(Error::invalid_argument("nonempty sketch store buffers must not be null"));
143 }
144 if (row_count > detail::maximum_batch_rows) {
145 return Err(Error::invalid_argument("sketch store has too many rows for one launch"));
146 }
147
148 detail::batch_scores_kernel<BucketCount>
149 <<<static_cast<uint32_t>(row_count), detail::block_size, 0, stream.get()>>>(
150 sketches.data(), static_cast<uint32_t>(row_count), scores_out.data()
151 );
152 return cuda_try(cudaGetLastError());
153}
Here is the call graph for this function:

◆ for_each_passing()

template<typename F >
Result< void > cuddl::for_each_passing ( batch_result_tile const &  tile,
cuda::stream_ref  stream,
F &&  f 
)

Calls f on the host with every passing result of a device tile, query-major and in reference order; synchronizes stream.

Results cross the bus in chunks through two pinned buffers, so the copy of chunk k + 1 runs while f visits chunk k. Threshold tiles are packed on the device first, so only passing results are copied. This streams where download materializes the whole tile.

Definition at line 615 of file reference_database.cuh.

615 {
616 detail::device_scope const scope{stream};
617 using pinned_counts = cuda::
618 buffer<packed_pairwise_counts, cuda::mr::host_accessible, cuda::mr::device_accessible>;
619 auto const memory =
620 detail::device_pinned_pool(stream.device(), detail::pinned_placement::device_node);
621 constexpr auto chunk_records = detail::visit_chunk_records;
622 auto const threshold = tile.pass_bits != nullptr;
623 auto const words = static_cast<size_t>(tile.query_count) * tile.words_per_query();
624
625 // Chunk plan, and for threshold tiles the host copies of the pass bits and word offsets.
626 std::vector<detail::visit_chunk> chunks;
627 std::vector<uint32_t> pass_bits;
628 std::vector<uint32_t> offsets;
629 cuda::device_buffer<uint32_t> device_offsets(
630 stream, cuda::device_default_memory_pool(stream.device())
631 );
632 if (!threshold) {
633 size_t record = 0U;
634 detail::visit_chunk chunk{};
635 for (uint32_t query = 0; query < tile.query_count; ++query) {
636 auto const query_id = tile.first_query_id + query;
637 auto const row = static_cast<size_t>(
638 tile.upper_triangle
639 ? (tile.reference_count > query_id + 1U ? tile.reference_count - query_id - 1U
640 : 0U)
642 );
643 if (record + row - chunk.first_record > chunk_records && query != chunk.first) {
644 chunk.last = query;
645 chunk.end_record = record;
646 chunks.push_back(chunk);
647 // NVCC 13.4 mislowers assignments that skip aggregate members.
648 chunk.first = query;
649 chunk.first_record = record;
650 }
651 record += row;
652 }
653 chunk.last = tile.query_count;
654 chunk.end_record = record;
655 if (record != 0U) chunks.push_back(chunk);
656 } else if (words != 0U) {
657 device_offsets = CUDDL_CUDA_TRY(
658 cuda::make_device_buffer<uint32_t>(stream, stream.device(), words, cuda::no_init)
659 );
660 auto const popcounts =
661 cuda::make_transform_iterator(tile.pass_bits, detail::word_popcount{});
662 size_t scan_bytes = 0;
664 cub::DeviceScan::ExclusiveSum(
665 nullptr,
666 scan_bytes,
667 popcounts,
668 device_offsets.data(),
669 static_cast<int64_t>(words),
670 stream.get()
671 )
672 );
673 auto scan_storage = CUDDL_CUDA_TRY(
674 cuda::make_device_buffer<uint8_t>(stream, stream.device(), scan_bytes, cuda::no_init)
675 );
677 cub::DeviceScan::ExclusiveSum(
678 scan_storage.data(),
679 scan_bytes,
680 popcounts,
681 device_offsets.data(),
682 static_cast<int64_t>(words),
683 stream.get()
684 )
685 );
686 pass_bits.resize(words);
687 offsets.resize(words);
689 cuda::copy_bytes(
690 stream, cuda::std::span{tile.pass_bits, words}, cuda::std::span{pass_bits}
691 )
692 );
693 CUDDL_CUDA_TRY(cuda::copy_bytes(stream, device_offsets, cuda::std::span{offsets}));
694 CUDDL_CUDA_TRY(stream.sync());
695 auto const total = static_cast<size_t>(offsets.back()) +
696 static_cast<size_t>(cuda::std::popcount(pass_bits.back()));
697 for (size_t first = 0U; first < words;) {
698 auto const base = static_cast<size_t>(offsets[first]);
699 // Whole words only: a word holds at most 32 results.
700 auto const last = static_cast<size_t>(
701 std::upper_bound(
702 offsets.begin() + static_cast<std::ptrdiff_t>(first) + 1,
703 offsets.end(),
704 static_cast<uint32_t>(base + chunk_records - 32U)
705 ) -
706 offsets.begin()
707 );
708 auto const end = last == words ? total : static_cast<size_t>(offsets[last]);
709 if (end != base) chunks.push_back({first, last, base, end});
710 first = last;
711 }
712 }
713 if (chunks.empty()) {
714 CUDDL_CUDA_TRY(stream.sync());
715 return Ok();
716 }
717
718 auto staging = CUDDL_CUDA_TRY(
719 cuda::make_device_buffer<packed_pairwise_counts>(
720 stream, stream.device(), threshold ? chunk_records : 0U, cuda::no_init
721 )
722 );
723 std::array<pinned_counts, 2> buffers{
724 pinned_counts(stream, memory, chunk_records, cuda::no_init),
725 pinned_counts(stream, memory, chunk_records, cuda::no_init),
726 };
727 std::array<cuda::event, 2> ready{cuda::event(stream.device()), cuda::event(stream.device())};
728 auto const enqueue = [&](size_t k) -> Result<void> {
729 auto const& chunk = chunks[k];
730 auto const records = chunk.end_record - chunk.first_record;
731 packed_pairwise_counts const* source = tile.results + chunk.first_record;
732 if (threshold) {
733 auto const kernel = tile.upper_triangle
734 ? detail::gather_passing_kernel<true, packed_pairwise_counts>
735 : detail::gather_passing_kernel<false, packed_pairwise_counts>;
736 kernel<<<
737 detail::warp_grid_blocks(chunk.last - chunk.first),
738 detail::block_size,
739 0,
740 stream.get()>>>(
741 tile.results,
742 nullptr,
743 tile.pass_bits,
744 device_offsets.data(),
745 tile.first_query_id,
746 tile.reference_count,
747 chunk.first,
748 chunk.last,
749 static_cast<uint32_t>(chunk.first_record),
750 staging.data(),
751 nullptr
752 );
753 CUDDL_CUDA_TRY(cudaGetLastError());
754 source = staging.data();
755 }
757 cuda::copy_bytes(
758 stream,
759 cuda::std::span{source, records},
760 cuda::std::span{buffers[k % 2U].data(), records}
761 )
762 );
763 ready[k % 2U].record(stream);
764 return Ok();
765 };
766
767 CUDDL_TRY(enqueue(0U));
768 for (size_t k = 0; k < chunks.size(); ++k) {
769 // Buffer (k + 1) % 2 last held chunk k - 1, which the host has finished visiting.
770 if (k + 1U < chunks.size()) CUDDL_TRY(enqueue(k + 1U));
771 CUDDL_CUDA_TRY(ready[k % 2U].sync());
772 auto const* packed = buffers[k % 2U].data();
773 auto const& chunk = chunks[k];
774 size_t next = 0U;
775 auto const emit = [&](uint32_t query_id, uint32_t reference_id) {
776 f(batch_search_result{
777 .query_id = query_id,
778 .reference_id = reference_id,
779 .counts = packed[next++].unpack(tile.bucket_count),
780 });
781 };
782 if (!threshold) {
783 for (auto query = static_cast<uint32_t>(chunk.first); query < chunk.last; ++query) {
784 auto const query_id = tile.first_query_id + query;
785 for (auto reference = tile.upper_triangle ? query_id + 1U : 0U;
786 reference < tile.reference_count;
787 ++reference) {
788 emit(query_id, reference);
789 }
790 }
791 continue;
792 }
793 auto const words_per_query = tile.words_per_query();
794 auto query = static_cast<uint32_t>(chunk.first / words_per_query);
795 auto column = static_cast<uint32_t>(chunk.first % words_per_query);
796 auto w = chunk.first;
797 while (w < chunk.last) {
798 // A transfer chunk may start or end inside a query row.
799 auto const row_end = std::min(chunk.last, w + words_per_query - column);
800 auto const query_id = tile.first_query_id + query;
801 for (auto base = column * 32U; w < row_end; ++w, base += 32U) {
802 for (auto bits = pass_bits[w]; bits != 0U; bits &= bits - 1U) {
803 emit(query_id, base + static_cast<uint32_t>(cuda::std::countr_zero(bits)));
804 }
805 }
806 ++query;
807 column = 0U;
808 }
809 }
810 return Ok();
811}
__global__ uint32_t reference_count
Definition kernels.cuh:437
auto const * query
Definition kernels.cuh:1030
__global__ uint32_t const uint32_t const * pass_bits
Definition kernels.cuh:1784
uint32_t column
Definition top_k.cuh:247
auto const total
Definition kernels.cuh:443
__global__ uint32_t const uint32_t const uint32_t const uint32_t uint32_t size_t size_t uint32_t SearchResult * packed
Definition kernels.cuh:1791
auto const words_per_query
Definition kernels.cuh:1795
auto *const bits
Definition kernels.cuh:1184
auto const row
Definition top_k.cuh:202
auto const query_id
Definition top_k.cuh:201
Here is the call graph for this function:

◆ gather_sketches()

Result< reference_database_file > cuddl::gather_sketches ( reference_database_file const &  mine,
genome_slice const &  slice,
MPI_Comm  comm 
)
inline

Gathers every rank's sketches of its genome_slice into one file on every rank.

Collective.

mine must hold the sketches of slice.mine(), in order, and every rank must have built with the same k, buckets, layout and blacklist; both are checked. The result equals a single-process build of slice.paths: rows in reference-ID order, labelled with the paths. Rows travel over MPI from host memory; every rank receives all of them.

Definition at line 285 of file distributed.cuh.

285 {
286 auto const counts = CUDDL_TRY(detail::slice_counts(mine, slice, comm));
287 std::vector<int> sizes(counts.begin(), counts.end()), offsets(counts.size());
288 std::exclusive_scan(sizes.begin(), sizes.end(), offsets.begin(), 0);
289 auto const buckets = mine.metadata().compatibility.bucket_count;
290 auto row = CUDDL_TRY(detail::row_type(buckets));
291 std::vector<uint16_t> rows(slice.paths.size() * buckets);
292 auto const gathered = detail::mpi_try(
293 MPI_Allgatherv(
294 mine.rows().data(),
295 static_cast<int>(mine.metadata().reference_count),
296 row,
297 rows.data(),
298 sizes.data(),
299 offsets.data(),
300 row,
301 comm
302 ),
303 "MPI_Allgatherv"
304 );
305 MPI_Type_free(&row);
306 CUDDL_TRY(gathered);
307 return detail::reference_file_assembly::make(mine, slice.paths, std::move(rows));
308}
Here is the call graph for this function:

◆ genome_paths()

Result< std::vector< std::filesystem::path > > cuddl::genome_paths ( std::filesystem::path const &  directory)
inline

The FASTA/FASTQ files under directory, searched recursively and sorted by path, which is the order that numbers reference IDs.

Recognised extensions are .fa, .fna, .fasta, .ffn, .frn, .fq and .fastq, in any case, optionally followed by .gz, .bgz or .bgzf. Entries are filtered by the type the directory reports and symlinks are kept without being followed, so listing a directory of symlinked genomes costs no per-file metadata lookup.

Definition at line 214 of file reference_database_file.cuh.

216 {
217 std::vector<std::filesystem::path> paths;
218 for (auto const& entry : std::filesystem::recursive_directory_iterator(
219 directory, std::filesystem::directory_options::skip_permission_denied
220 )) {
221 if (!entry.is_symlink() && !entry.is_regular_file()) continue;
222 auto name = entry.path().filename().string();
223 std::ranges::transform(name, name.begin(), [](unsigned char c) {
224 return static_cast<char>(std::tolower(c));
225 });
226 auto extension = std::filesystem::path(name).extension().string();
227 if (extension == ".gz" || extension == ".bgz" || extension == ".bgzf") {
228 extension = std::filesystem::path(name).stem().extension().string();
229 }
230 if (extension == ".fa" || extension == ".fna" || extension == ".fasta" ||
231 extension == ".ffn" || extension == ".frn" || extension == ".fq" ||
232 extension == ".fastq") {
233 paths.push_back(entry.path());
234 }
235 }
236 std::ranges::sort(paths);
237 return paths;
238} catch (std::filesystem::filesystem_error const& error) {
239 return Err(Error::resource(error.what()));
240}
Here is the caller graph for this function:

◆ list_genomes()

Result< std::vector< std::filesystem::path > > cuddl::list_genomes ( std::filesystem::path const &  input)
inline

Paths from a directory (see genome_paths) or from a text file with one path per line, in the file's order.

Definition at line 244 of file reference_database_file.cuh.

246 {
247 if (std::filesystem::is_directory(input)) return genome_paths(input);
248 std::ifstream list(input);
249 if (!list) return Err(Error::resource("cannot read genome list " + input.string()));
250 std::vector<std::filesystem::path> paths;
251 for (std::string line; std::getline(list, line);) {
252 if (!line.empty()) paths.emplace_back(line);
253 }
254 return paths;
255}
Result< std::vector< std::filesystem::path > > genome_paths(std::filesystem::path const &directory)
The FASTA/FASTQ files under directory, searched recursively and sorted by path, which is the order th...
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Ok()

Result< void > cuddl::Ok ( )
inlinenoexcept

Success return for Result<void>; same as return {}.

Definition at line 181 of file error.hpp.

181 {
182 return {};
183}
Here is the caller graph for this function:

◆ parse_fasta_file()

Result< fasta_parse_result > cuddl::parse_fasta_file ( std::string const &  path,
uint32_t  k,
unsigned  threads = std::thread::hardware_concurrency() 
)
inline

Parses a FASTA/FASTQ file's sequence bases into packed canonical k-mers of length k.

Gzip/BGZF is detected by magic bytes and decompressed before parsing. Format is detected from the first record header; FASTQ qualities are validated and ignored. This is the FASTX entry point. It reads every record as one combined genome and emits a packed canonical k-mer per contiguous run of k valid bases; an invalid or ambiguous base breaks the rolling window, and no k-mer spanning it is emitted. Record boundaries also reset the window, without contributing to invalid_windows. See detail::parse_fasta.

Parameters
pathFASTA/FASTQ file to parse.
kK-mer length.
threadsWorker count for parallel files, defaulting to std::thread::hardware_concurrency(); files at or below 1 MiB parse serially.
Returns
Parsed k-mers and counts, or an error if the file cannot be opened.

Definition at line 34 of file fastx.hpp.

38 {
39 return detail::parse_fasta(path, k, threads);
40}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ search_all_to_all() [1/2]

template<uint32_t K, size_t BucketCount, typename Layout , typename OnTile >
Result< void > cuddl::search_all_to_all ( replicated_database< K, BucketCount, Layout > &  group,
OnTile &&  on_tile,
MPI_Comm  comm 
)

Compares every pair of group's references, sharing the tiles among the ranks of comm; collective.

Every rank calls it with a replicated_database of the same rows. Each rank delivers the tiles it claims to on_tile, as replicated_database::search_all_to_all does, and every pair is delivered on exactly one rank. The ranks share one shared_tile_counter for the call.

Definition at line 475 of file distributed.cuh.

479 {
480 return detail::shared_search(comm, [&](replica_search split) {
481 return group.search_all_to_all(on_tile, std::move(split));
482 });
483}
How a replicated_database search splits and delivers its tiles.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ search_all_to_all() [2/2]

template<uint32_t K, size_t BucketCount, typename Layout , typename OnTile >
Result< void > cuddl::search_all_to_all ( replicated_database< K, BucketCount, Layout > &  group,
OnTile &&  on_tile,
search_options  options,
MPI_Comm  comm 
)

The threshold form of search_all_to_all: only pairs passing options reach on_tile.

group needs its index (replicated_database::build_index); collective.

Definition at line 488 of file distributed.cuh.

493 {
494 return detail::shared_search(comm, [&](replica_search split) {
495 return group.search_all_to_all(on_tile, options, std::move(split));
496 });
497}
Here is the call graph for this function:

◆ select_top_k()

std::vector< batch_search_result > cuddl::select_top_k ( std::vector< batch_search_result >  rows,
uint32_t  k 
)
inline

The k best rows of each query among rows, as top_k_search ranks them, best first and query by query.

Merges the downloads of collectors that saw disjoint tiles.

Definition at line 533 of file top_k.cuh.

533 {
534 std::ranges::sort(rows, [](batch_search_result const& a, batch_search_result const& b) {
535 if (a.query_id != b.query_id) return a.query_id < b.query_id;
536 return detail::top_k_key(a.counts, a.reference_id) >
537 detail::top_k_key(b.counts, b.reference_id);
538 });
539 std::vector<batch_search_result> kept;
540 for (size_t first = 0U; first < rows.size();) {
541 auto last = first;
542 while (last < rows.size() && rows[last].query_id == rows[first].query_id) ++last;
543 kept.insert(
544 kept.end(),
545 rows.begin() + static_cast<std::ptrdiff_t>(first),
546 rows.begin() + static_cast<std::ptrdiff_t>(std::min<size_t>(last, first + k))
547 );
548 first = last;
549 }
550 return kept;
551}
Stable query/reference IDs and their exact query-relative bucket comparison counts.
Here is the call graph for this function:

◆ slice_genomes()

Result< genome_slice > cuddl::slice_genomes ( std::filesystem::path const &  input,
MPI_Comm  comm,
unsigned  workers = default_parser_workers() 
)
inline

Lists a genome collection once and splits it into one contiguous slice per rank of comm, of about equal file bytes.

Collective.

Rank 0 lists input, a directory (see genome_paths) or a text file with one path per line, and shares the list. Every rank then looks up the sizes of an equal share of the files with workers threads, because on a parallel file system each lookup waits for a metadata round trip, and the ranks exchange them. Slices follow path order, so concatenating them in rank order gives the reference IDs of a single-process build. Bytes stand in for sketching work; a rank may get no genomes when there are fewer genomes than ranks.

Definition at line 159 of file distributed.cuh.

163 {
164 if (workers == 0U)
165 return Err(Error::invalid_argument("slice_genomes needs at least one worker"));
166 int rank = 0, size = 1;
167 CUDDL_TRY(detail::mpi_try(MPI_Comm_rank(comm, &rank), "MPI_Comm_rank"));
168 CUDDL_TRY(detail::mpi_try(MPI_Comm_size(comm, &size), "MPI_Comm_size"));
169 genome_slice slice;
170 slice.paths = CUDDL_TRY(detail::share_listing(input, rank, comm));
171 auto const& paths = slice.paths;
172 if (paths.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
173 return Err(Error::resource("genome collection exceeds MPI counts"));
174 }
175
176 auto const share = [&](int r) {
177 return paths.size() * static_cast<size_t>(r) / static_cast<size_t>(size);
178 };
179 auto const own = share(rank);
180 std::vector<uint64_t> mine(share(rank + 1) - own);
181 std::vector<std::error_code> errors(mine.size());
182 {
183 std::vector<std::jthread> threads;
184 for (unsigned t = 0; t < workers && t < mine.size(); ++t) {
185 threads.emplace_back([&, t] {
186 for (size_t i = t; i < mine.size(); i += workers) {
187 mine[i] = std::filesystem::file_size(paths[own + i], errors[i]);
188 }
189 });
190 }
191 }
192 auto sized = [&]() -> Result<void> {
193 for (size_t i = 0; i < mine.size(); ++i) {
194 if (errors[i]) {
195 return Err(Error::resource(paths[own + i].string() + ": " + errors[i].message()));
196 }
197 }
198 return Ok();
199 }();
200 CUDDL_TRY(detail::agree(std::move(sized), comm));
201
202 std::vector<int> counts(size), offsets(size);
203 for (int r = 0; r < size; ++r) {
204 offsets[r] = static_cast<int>(share(r));
205 counts[r] = static_cast<int>(share(r + 1) - share(r));
206 }
207 slice.bytes.resize(paths.size());
208 CUDDL_TRY(
209 detail::mpi_try(
210 MPI_Allgatherv(
211 mine.data(),
212 static_cast<int>(mine.size()),
213 MPI_UINT64_T,
214 slice.bytes.data(),
215 counts.data(),
216 offsets.data(),
217 MPI_UINT64_T,
218 comm
219 ),
220 "MPI_Allgatherv"
221 )
222 );
223 auto const total = std::accumulate(slice.bytes.begin(), slice.bytes.end(), uint64_t{0});
224 slice.first = detail::slice_begin(slice.bytes, total, rank, size);
225 slice.last =
226 rank + 1 == size ? paths.size() : detail::slice_begin(slice.bytes, total, rank + 1, size);
227 return slice;
228}
One rank's share of a genome collection split across an MPI communicator.
std::vector< std::filesystem::path > paths
Every genome of the collection in reference-ID order; the same on every rank.
size_t first
This rank's genomes are paths[first, last).
std::vector< uint64_t > bytes
Size of each genome's file in bytes.
Here is the call graph for this function:

◆ weighted_kmer_identity()

__host__ __device__ constexpr float cuddl::weighted_kmer_identity ( pairwise_counts const &  counts)
constexprnoexcept

Weighted k-mer identity of a pair, equal / max(equal + min(lower, higher), 6), as sketch::wkid computes it, or 0 when no bucket is comparable.

The divisor's floor of six suppresses similarity from sparse random collisions.

Definition at line 83 of file pairwise_counts.cuh.

85 {
86 auto const divisor =
87 counts.equal + (counts.lower < counts.higher ? counts.lower : counts.higher);
88 if (divisor == 0U) return 0.0F;
89 return static_cast<float>(counts.equal) / static_cast<float>(divisor < 6U ? 6U : divisor);
90}
Here is the caller graph for this function: