Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions include/cuco/bloom_filter.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <cuco/bloom_filter_policy.cuh>
#include <cuco/bloom_filter_ref.cuh>
#include <cuco/detail/storage/storage_base.cuh>
#include <cuco/detail/utility/strong_type.cuh>
#include <cuco/extent.cuh>
#include <cuco/hash_functions.cuh>
#include <cuco/utility/allocator.hpp>
Expand All @@ -16,6 +17,7 @@
#include <cuda/atomic>
#include <cuda/std/array>
#include <cuda/std/cstddef>
#include <cuda/std/type_traits>
#include <cuda/stream_ref>

#include <cstddef>
Expand All @@ -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.
*
Expand Down Expand Up @@ -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 <class E = Extent,
class = cuda::std::enable_if_t<
cuda::std::is_same_v<E, cuco::extent<typename Extent::value_type>>>>
__host__ explicit bloom_filter(bloom_filter_bytes size_bytes,
cuda_thread_scope<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.
*
Expand Down
37 changes: 37 additions & 0 deletions include/cuco/detail/bloom_filter/bloom_filter.inl
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,53 @@

#pragma once

#include <cuco/detail/error.hpp>
#include <cuco/detail/storage/storage_base.cuh>
#include <cuco/utility/cuda_thread_scope.cuh>

#include <cuda/atomic>
#include <cuda/std/__algorithm/min.h>
#include <cuda/std/limits>
#include <cuda/stream_ref>

#include <cstddef>

namespace cuco {

template <class Key, class Extent, cuda::thread_scope Scope, class Policy, class Allocator>
template <class E, class>
__host__ bloom_filter<Key, Extent, Scope, Policy, Allocator>::bloom_filter(
bloom_filter_bytes size_bytes,
cuda_thread_scope<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<size_type>(
cuda::std::min(size_bytes.value, max_size()) / block_bytes)};
}(),
scope,
policy,
alloc,
stream}
{
}

template <class Key, class Extent, cuda::thread_scope Scope, class Policy, class Allocator>
[[nodiscard]] __host__ constexpr std::size_t
bloom_filter<Key, Extent, Scope, Policy, Allocator>::max_size() noexcept
{
constexpr auto block_bytes = sizeof(typename ref_type<>::filter_block_type);
constexpr auto max_blocks = cuda::std::min(
static_cast<std::size_t>(Policy::max_filter_blocks),
cuda::std::min(static_cast<std::size_t>(cuda::std::numeric_limits<size_type>::max()),
cuda::std::numeric_limits<std::size_t>::max() / block_bytes));
return max_blocks * block_bytes;
}

template <class Key, class Extent, cuda::thread_scope Scope, class Policy, class Allocator>
__host__ bloom_filter<Key, Extent, Scope, Policy, Allocator>::bloom_filter(Extent num_blocks,
cuda_thread_scope<Scope>,
Expand Down
6 changes: 5 additions & 1 deletion tests/bloom_filter/unique_sequence_test.cu
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,12 @@ TEMPLATE_TEST_CASE_SIG(
using filter_type =
cuco::bloom_filter<Key, cuco::extent<size_t>, 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<std::size_t>(filter.block_extent()) == num_blocks);

test_unique_sequence(filter, num_keys);
}