diff --git a/include/cuco/bloom_filter.cuh b/include/cuco/bloom_filter.cuh index 38f841db8..607462e3d 100644 --- a/include/cuco/bloom_filter.cuh +++ b/include/cuco/bloom_filter.cuh @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -24,6 +26,11 @@ namespace cuco { +/** + * @brief A strong type wrapper for specifying a Bloom filter storage budget in bytes. + */ +CUCO_DEFINE_STRONG_TYPE(bloom_filter_bytes, std::size_t) + /** * @brief A GPU-accelerated Bloom filter. * @@ -116,6 +123,40 @@ class bloom_filter { Allocator const& alloc = {}, cuda::stream_ref stream = cuda::stream_ref{cudaStream_t{nullptr}}); + /** + * @brief Constructs a Bloom filter within a storage budget in bytes. + * + * The allocated size is rounded down to a whole number of filter blocks and capped at + * `max_size()`. + * + * @note This overload requires a dynamic extent. + * + * @throws cuco::logic_error If the budget cannot accommodate one filter block + * + * @param size_bytes Storage budget in bytes + * @param scope The scope in which operations will be performed + * @param policy Fingerprint generation policy + * @param alloc Allocator used for allocating device-accessible storage + * @param stream CUDA stream used to initialize the filter + */ + template >>> + __host__ explicit bloom_filter(bloom_filter_bytes size_bytes, + cuda_thread_scope scope = {}, + Policy const& policy = {}, + Allocator const& alloc = {}, + cuda::stream_ref stream = cuda::stream_ref{cudaStream_t{nullptr}}); + + /** + * @brief Returns the maximum storage size in bytes supported by the policy and size type. + * + * @note This limit does not account for available device memory or a particular static extent. + * + * @return Maximum storage size in bytes + */ + [[nodiscard]] __host__ static constexpr std::size_t max_size() noexcept; + /** * @brief Erases all information from the filter. * diff --git a/include/cuco/detail/bloom_filter/bloom_filter.inl b/include/cuco/detail/bloom_filter/bloom_filter.inl index 3b62d1041..2a6fa228a 100644 --- a/include/cuco/detail/bloom_filter/bloom_filter.inl +++ b/include/cuco/detail/bloom_filter/bloom_filter.inl @@ -5,16 +5,53 @@ #pragma once +#include #include #include #include +#include +#include #include #include namespace cuco { +template +template +__host__ bloom_filter::bloom_filter( + bloom_filter_bytes size_bytes, + cuda_thread_scope scope, + Policy const& policy, + Allocator const& alloc, + cuda::stream_ref stream) + : bloom_filter{[size_bytes] { + constexpr auto block_bytes = sizeof(typename ref_type<>::filter_block_type); + CUCO_EXPECTS(size_bytes.value >= block_bytes, + "Storage size must accommodate at least one filter block"); + return extent_type{static_cast( + cuda::std::min(size_bytes.value, max_size()) / block_bytes)}; + }(), + scope, + policy, + alloc, + stream} +{ +} + +template +[[nodiscard]] __host__ constexpr std::size_t +bloom_filter::max_size() noexcept +{ + constexpr auto block_bytes = sizeof(typename ref_type<>::filter_block_type); + constexpr auto max_blocks = cuda::std::min( + static_cast(Policy::max_filter_blocks), + cuda::std::min(static_cast(cuda::std::numeric_limits::max()), + cuda::std::numeric_limits::max() / block_bytes)); + return max_blocks * block_bytes; +} + template __host__ bloom_filter::bloom_filter(Extent num_blocks, cuda_thread_scope, diff --git a/tests/bloom_filter/unique_sequence_test.cu b/tests/bloom_filter/unique_sequence_test.cu index 92b546f2f..44e702881 100644 --- a/tests/bloom_filter/unique_sequence_test.cu +++ b/tests/bloom_filter/unique_sequence_test.cu @@ -87,8 +87,12 @@ TEMPLATE_TEST_CASE_SIG( using filter_type = cuco::bloom_filter, cuda::thread_scope_device, Policy>; constexpr size_type num_keys{400}; + constexpr std::size_t num_blocks{1000}; + constexpr auto block_bytes = sizeof(typename filter_type::template ref_type<>::filter_block_type); - auto filter = filter_type{1000}; + STATIC_REQUIRE(filter_type::max_size() == Policy::max_filter_blocks * block_bytes); + auto filter = filter_type{cuco::bloom_filter_bytes{num_blocks * block_bytes + block_bytes - 1}}; + REQUIRE(static_cast(filter.block_extent()) == num_blocks); test_unique_sequence(filter, num_keys); }