Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
836e77a
First draft
sleeepyjack Jan 24, 2024
d3a1e2f
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Jan 24, 2024
6718560
Code style
sleeepyjack Jan 24, 2024
c59744e
Resolve merge conflicts
sleeepyjack Jan 24, 2024
b7533a0
Initialize shmem atomics through placement new
sleeepyjack Jan 24, 2024
f4bdac2
Improve naming
sleeepyjack Jan 24, 2024
cea2afb
Move some functionality to storage class
sleeepyjack Jan 24, 2024
0f0bd3f
Add inline docs for public APIs
sleeepyjack Jan 25, 2024
b21dcd1
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Jan 25, 2024
1c780c2
Add benchmark
sleeepyjack Jan 25, 2024
a83a3f3
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Jan 30, 2024
b478e01
Remove scope ctor parameter for now
sleeepyjack Jan 30, 2024
e3d401a
Update benchmark
sleeepyjack Jan 30, 2024
56520a6
Select cg reduce impl based on nvcc version
sleeepyjack Jan 31, 2024
3673772
Re-format tuning header
sleeepyjack Jan 31, 2024
799284e
Implement HLL++ bias correction step
sleeepyjack Feb 1, 2024
758977c
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Feb 1, 2024
abbeffa
Extend examples and fix some bugs along the way
sleeepyjack Feb 1, 2024
86d4618
Refactor thresholds
sleeepyjack Feb 1, 2024
d6a9a4e
Initialize shmem storage using placement new
sleeepyjack Feb 1, 2024
891d606
Add unit test
sleeepyjack Feb 1, 2024
3544195
Remove experimental cg async reduce since it is buggy
sleeepyjack Feb 1, 2024
3506ecb
Fix bit-shifting bug that lead to high error rates
sleeepyjack Feb 2, 2024
919d0ab
Storage cleanups
sleeepyjack Feb 2, 2024
12301df
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Feb 2, 2024
52f6e09
Update readme
sleeepyjack Feb 2, 2024
0a0119d
Fix typo
sleeepyjack Feb 2, 2024
ab50bed
Fix typo
sleeepyjack Feb 2, 2024
68d2df0
Apply suggestions from code review
sleeepyjack Feb 3, 2024
4567169
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Feb 6, 2024
93f68a2
Use CUDART_VERSION instead of (__CUDACC_VER_MAJOR__
sleeepyjack Feb 6, 2024
03a8572
Apply suggestions from code review
sleeepyjack Feb 6, 2024
33f7baf
Enable Precision>18; fix some bugs, extend tests.
sleeepyjack Feb 16, 2024
6e3683f
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Feb 16, 2024
b1253bf
Remove storage class and move host implementations to ref class
sleeepyjack Mar 13, 2024
64a5b70
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Mar 13, 2024
22c083d
Remove storage class
sleeepyjack Mar 14, 2024
56cdc6b
Add vectorized add kernel
sleeepyjack Mar 15, 2024
b8dc849
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Mar 15, 2024
9b4b612
Add missing kernel config
sleeepyjack Mar 15, 2024
30bd79d
Make tuning arrs accessible in non-constexpr context
sleeepyjack Mar 15, 2024
e93c248
Allow wider vector sizes
sleeepyjack Mar 15, 2024
204b8e2
Fix processing of remaining items
sleeepyjack Mar 15, 2024
fe1cf5a
Guard invoke_one with macro
sleeepyjack Mar 15, 2024
ae9e77c
Specify sketch size/precision at runtime
sleeepyjack Mar 18, 2024
65ff70a
Pre-compute register mask
sleeepyjack Mar 18, 2024
8068799
Fix unit test
sleeepyjack Mar 19, 2024
04c303d
Add sketch_size_kb strong type and fix stupid bug where I called a st…
sleeepyjack Mar 20, 2024
a7036ae
Fix benchmark
sleeepyjack Mar 20, 2024
3e25da7
More robust error estimation in benchmark
sleeepyjack Mar 20, 2024
e5d5112
Benchmark gmem fallback kernel
sleeepyjack Mar 20, 2024
99c0dee
Rename max_sketch_size_kb -> sketch_size_kb
sleeepyjack Mar 20, 2024
aeaecf4
Improve error handling and docs
sleeepyjack Mar 20, 2024
55fa312
Cleanup finalizer
sleeepyjack Mar 20, 2024
2229c68
Use double reduction
sleeepyjack Mar 20, 2024
156a843
Use .estimate() in device ref example
sleeepyjack Mar 20, 2024
80dde95
Add device ref test
sleeepyjack Mar 20, 2024
730bf73
Restructure to reduce fp error
sleeepyjack Mar 20, 2024
c50e795
Rename parameter for other estimator ref
sleeepyjack Mar 20, 2024
d5595da
Update benchmark
sleeepyjack Mar 20, 2024
16ad77a
Rebind allocator to register_type to ensure proper alignment
sleeepyjack Mar 20, 2024
b501a32
Use cudaMemcpyDefault
sleeepyjack Mar 20, 2024
0bf0a88
Mention alignment requirements in device_ref_example
sleeepyjack Mar 20, 2024
d03120c
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Mar 20, 2024
a361360
Pass T instead of Estimator to benchmark
sleeepyjack Mar 20, 2024
66870a7
Fix typo in benchmark script
sleeepyjack Mar 20, 2024
b990dca
Rename hash function
sleeepyjack Mar 21, 2024
c87309e
Use placement new to initialize sketch
sleeepyjack Mar 21, 2024
03d4b41
Remove custom_deleter member
sleeepyjack Mar 21, 2024
7de06fb
Rename sketch_size.hpp -> sktech_size.cuh
sleeepyjack Mar 21, 2024
185d3c4
Use std::abs
sleeepyjack Mar 21, 2024
023d080
Use std::vector instead of thrust::host_vector>
sleeepyjack Mar 21, 2024
53cdf37
Add note about shmem alignment
sleeepyjack Mar 21, 2024
2a81714
Remove comment
sleeepyjack Mar 21, 2024
d859b39
Remove device-sided error handling since it hurts performance
sleeepyjack Mar 21, 2024
43be0f0
Constexpr all the things!
sleeepyjack Mar 21, 2024
fbd6dab
Add constructor overload which takes the desired standard deviation
sleeepyjack Mar 22, 2024
dfe1a07
Remove stray include
sleeepyjack Mar 22, 2024
2629adc
Bugfixes
sleeepyjack Mar 23, 2024
1ad97e2
Fix merge
sleeepyjack Mar 27, 2024
3b0da20
Add Spark parity tests
sleeepyjack Mar 27, 2024
f80509f
Fix error calculation
sleeepyjack Mar 27, 2024
bbb7258
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Apr 3, 2024
b61a2db
Move include/cuco/sentinel.cuh -> include/cuco/types.cuh
sleeepyjack Apr 3, 2024
9b0ee68
Move HLL-related strong types to types.cuh
sleeepyjack Apr 3, 2024
75cd967
Apparently Doxygen has become even pickier...
sleeepyjack Apr 3, 2024
9436931
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack Apr 3, 2024
6929f65
Clean up device ref example
sleeepyjack Apr 3, 2024
a496f9e
Update godbolt links
sleeepyjack Apr 3, 2024
7fecd7b
Clean up unique sequence unit test
sleeepyjack Apr 3, 2024
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
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ ConfigureExample(STATIC_MAP_DEVICE_SIDE_EXAMPLE "${CMAKE_CURRENT_SOURCE_DIR}/sta
ConfigureExample(STATIC_MAP_CUSTOM_TYPE_EXAMPLE "${CMAKE_CURRENT_SOURCE_DIR}/static_map/custom_type_example.cu")
ConfigureExample(STATIC_MAP_COUNT_BY_KEY_EXAMPLE "${CMAKE_CURRENT_SOURCE_DIR}/static_map/count_by_key_example.cu")
ConfigureExample(STATIC_MULTIMAP_HOST_BULK_EXAMPLE "${CMAKE_CURRENT_SOURCE_DIR}/static_multimap/host_bulk_example.cu")
ConfigureExample(DISTINCT_COUNT_ESTIMATOR_HOST_BULK_EXAMPLE "${CMAKE_CURRENT_SOURCE_DIR}/distinct_count_estimator/host_bulk_example.cu")
61 changes: 61 additions & 0 deletions examples/distinct_count_estimator/host_bulk_example.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cuco/distinct_count_estimator.cuh>

#include <thrust/device_vector.h>
#include <thrust/sequence.h>

#include <cstddef>
#include <iostream>

int main()
{
using T = int;
std::size_t constexpr num_items = 1ull << 30; // 4GB

thrust::device_vector<T> items(num_items);
// create a vector of distinct items
thrust::sequence(items.begin(), items.end(), 0);

cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);

cuco::distinct_count_estimator<T> estimator;
cudaEventRecord(start);
// add all items to the estimator
estimator.add(items.begin(), items.end());
// after the estimator has seen all items, we can calculate the cardinality
std::size_t const estimated_cardinality = estimator.estimate();
cudaEventRecord(stop);
cudaEventSynchronize(stop);

float milliseconds = 0;
cudaEventElapsedTime(&milliseconds, start, stop);
Comment thread
sleeepyjack marked this conversation as resolved.
Outdated
float input_size_gb = num_items * sizeof(T) / 1073741824.0f;
float throughput = input_size_gb / (milliseconds / 1000.0f);

std::cout << "True cardinality:\t" << num_items << "\nEstimated cardinality:\t"
<< estimated_cardinality << "\nRelative error:\t"
<< abs(static_cast<double>(num_items) - static_cast<double>(estimated_cardinality)) /
Comment thread
sleeepyjack marked this conversation as resolved.
Outdated
num_items
<< "\nData size:\t" << input_size_gb << "GB"
<< "\nElapsed time:\t" << milliseconds << "ms"
<< "\nMemory throughput\t" << throughput << "GB/s" << std::endl;

cudaEventDestroy(start);
cudaEventDestroy(stop);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace cuco {

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
constexpr distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::distinct_count_estimator(
cuco::cuda_thread_scope<Scope> scope,
Hash const& hash,
Allocator const& alloc,
cuco::cuda_stream_ref stream)
: impl_{std::make_unique<impl_type>(scope, hash, alloc, stream)}
{
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::clear_async(
cuco::cuda_stream_ref stream) noexcept
{
this->impl_->clear_async(stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::clear(
cuco::cuda_stream_ref stream)
{
this->impl_->clear(stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
template <class InputIt>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::add_async(
InputIt first, InputIt last, cuco::cuda_stream_ref stream) noexcept
{
this->impl_->add_async(first, last, stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
template <class InputIt>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::add(
InputIt first, InputIt last, cuco::cuda_stream_ref stream)
{
this->impl_->add(first, last, stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
template <cuda::thread_scope OtherScope, class OtherAllocator>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::merge_async(
distinct_count_estimator<T, Precision, OtherScope, Hash, OtherAllocator> const& other,
cuco::cuda_stream_ref stream) noexcept
{
this->impl_->merge_async(other, stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
template <cuda::thread_scope OtherScope, class OtherAllocator>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::merge(
distinct_count_estimator<T, Precision, OtherScope, Hash, OtherAllocator> const& other,
cuco::cuda_stream_ref stream)
{
this->impl_->merge(other, stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
template <cuda::thread_scope OtherScope>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::merge_async(
ref_type<OtherScope> const& other, cuco::cuda_stream_ref stream) noexcept
{
this->impl_->merge_async(other, stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
template <cuda::thread_scope OtherScope>
void distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::merge(
ref_type<OtherScope> const& other, cuco::cuda_stream_ref stream)
{
this->impl_->merge(other, stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
std::size_t distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::estimate(
cuco::cuda_stream_ref stream) const
{
return this->impl_->estimate(stream);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash, class Allocator>
typename distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::ref_type<>
distinct_count_estimator<T, Precision, Scope, Hash, Allocator>::ref() const noexcept
{
return this->impl_->ref();
}
} // namespace cuco
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace cuco {

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash>
__host__ __device__ constexpr distinct_count_estimator_ref<T, Precision, Scope, Hash>::
distinct_count_estimator_ref(storage_type& storage,
cuco::cuda_thread_scope<Scope> scope,
Hash const& hash) noexcept
: impl_{storage, scope, hash}
{
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash>
template <class CG>
__device__ void distinct_count_estimator_ref<T, Precision, Scope, Hash>::clear(
CG const& group) noexcept
{
this->impl_.clear(group);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash>
__device__ void distinct_count_estimator_ref<T, Precision, Scope, Hash>::add(T const& item) noexcept
{
this->impl_.add(item);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash>
template <class CG, cuda::thread_scope OtherScope>
__device__ void distinct_count_estimator_ref<T, Precision, Scope, Hash>::merge(
CG const& group,
distinct_count_estimator_ref<T, Precision, OtherScope, Hash> const& other) noexcept
{
this->impl_.merge(group, other);
}

template <class T, int32_t Precision, cuda::thread_scope Scope, class Hash>
__device__ std::size_t distinct_count_estimator_ref<T, Precision, Scope, Hash>::estimate(
cooperative_groups::thread_block const& group) const noexcept
{
this->impl_.estimate(group);
}
} // namespace cuco
79 changes: 79 additions & 0 deletions include/cuco/detail/hyperloglog/finalizer.cuh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2024, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <cuco/detail/hyperloglog/tuning.cuh>

#include <cuda/std/cmath>

namespace cuco::hyperloglog_ns::detail {
template <int32_t Precision>
class finalizer {
// this minimum number of registers is required by HLL++
static_assert(Precision >= 4, "Precision must be greater or equal to 4");

public:
__host__ __device__ static double constexpr finalize(double z, int v) noexcept
{
auto e = alpha_mm() / z;
// TODO remove test code
// printf("raw e: %lf\n", e);

if (v > 0) {
// Use linear counting for small cardinality estimates.
double const h = m * log(static_cast<double>(m) / v);
// HLL++ is defined only when p < 19, otherwise we need to fallback to HLL.
// The threshold `2.5 * m` is from the original HLL algorithm.
if ((Precision < 19 and h <= thresholds[Precision - 4]) or e <= 2.5 * m) {
e = h;
} else {
e = bias_corrected_estimate(e);
}
} else {
e = bias_corrected_estimate(e);
}

return cuda::std::round(e);
}

private:
static auto constexpr m = (1 << Precision);

__host__ __device__ static double constexpr alpha_mm() noexcept
{
if constexpr (m == 16) {
return 0.673 * m * m;
} else if constexpr (m == 32) {
return 0.697 * m * m;
} else if constexpr (m == 64) {
return 0.709 * m * m;
} else {
return (0.7213 / (1.0 + 1.079 / m)) * m * m;
}
}

__host__ __device__ static double constexpr bias_corrected_estimate(double e) noexcept
{
if constexpr (Precision < 19) {
if (e < 5.0 * m) { return e - bias(e); }
}
return e;
}

// TODO implement HLL++ bias correction
__host__ __device__ static double constexpr bias(double e) noexcept { return e * 0; }
};
} // namespace cuco::hyperloglog_ns::detail
Loading