-
Notifications
You must be signed in to change notification settings - Fork 113
distinct_count_estimator aka HyperLogLog++ #429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
90 commits
Select commit
Hold shift + click to select a range
836e77a
First draft
sleeepyjack d3a1e2f
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack 6718560
Code style
sleeepyjack c59744e
Resolve merge conflicts
sleeepyjack b7533a0
Initialize shmem atomics through placement new
sleeepyjack f4bdac2
Improve naming
sleeepyjack cea2afb
Move some functionality to storage class
sleeepyjack 0f0bd3f
Add inline docs for public APIs
sleeepyjack b21dcd1
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack 1c780c2
Add benchmark
sleeepyjack a83a3f3
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack b478e01
Remove scope ctor parameter for now
sleeepyjack e3d401a
Update benchmark
sleeepyjack 56520a6
Select cg reduce impl based on nvcc version
sleeepyjack 3673772
Re-format tuning header
sleeepyjack 799284e
Implement HLL++ bias correction step
sleeepyjack 758977c
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack abbeffa
Extend examples and fix some bugs along the way
sleeepyjack 86d4618
Refactor thresholds
sleeepyjack d6a9a4e
Initialize shmem storage using placement new
sleeepyjack 891d606
Add unit test
sleeepyjack 3544195
Remove experimental cg async reduce since it is buggy
sleeepyjack 3506ecb
Fix bit-shifting bug that lead to high error rates
sleeepyjack 919d0ab
Storage cleanups
sleeepyjack 12301df
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack 52f6e09
Update readme
sleeepyjack 0a0119d
Fix typo
sleeepyjack ab50bed
Fix typo
sleeepyjack 68d2df0
Apply suggestions from code review
sleeepyjack 4567169
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack 93f68a2
Use CUDART_VERSION instead of (__CUDACC_VER_MAJOR__
sleeepyjack 03a8572
Apply suggestions from code review
sleeepyjack 33f7baf
Enable Precision>18; fix some bugs, extend tests.
sleeepyjack 6e3683f
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack b1253bf
Remove storage class and move host implementations to ref class
sleeepyjack 64a5b70
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack 22c083d
Remove storage class
sleeepyjack 56cdc6b
Add vectorized add kernel
sleeepyjack b8dc849
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack 9b4b612
Add missing kernel config
sleeepyjack 30bd79d
Make tuning arrs accessible in non-constexpr context
sleeepyjack e93c248
Allow wider vector sizes
sleeepyjack 204b8e2
Fix processing of remaining items
sleeepyjack fe1cf5a
Guard invoke_one with macro
sleeepyjack ae9e77c
Specify sketch size/precision at runtime
sleeepyjack 65ff70a
Pre-compute register mask
sleeepyjack 8068799
Fix unit test
sleeepyjack 04c303d
Add sketch_size_kb strong type and fix stupid bug where I called a st…
sleeepyjack a7036ae
Fix benchmark
sleeepyjack 3e25da7
More robust error estimation in benchmark
sleeepyjack e5d5112
Benchmark gmem fallback kernel
sleeepyjack 99c0dee
Rename max_sketch_size_kb -> sketch_size_kb
sleeepyjack aeaecf4
Improve error handling and docs
sleeepyjack 55fa312
Cleanup finalizer
sleeepyjack 2229c68
Use double reduction
sleeepyjack 156a843
Use .estimate() in device ref example
sleeepyjack 80dde95
Add device ref test
sleeepyjack 730bf73
Restructure to reduce fp error
sleeepyjack c50e795
Rename parameter for other estimator ref
sleeepyjack d5595da
Update benchmark
sleeepyjack 16ad77a
Rebind allocator to register_type to ensure proper alignment
sleeepyjack b501a32
Use cudaMemcpyDefault
sleeepyjack 0bf0a88
Mention alignment requirements in device_ref_example
sleeepyjack d03120c
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack a361360
Pass T instead of Estimator to benchmark
sleeepyjack 66870a7
Fix typo in benchmark script
sleeepyjack b990dca
Rename hash function
sleeepyjack c87309e
Use placement new to initialize sketch
sleeepyjack 03d4b41
Remove custom_deleter member
sleeepyjack 7de06fb
Rename sketch_size.hpp -> sktech_size.cuh
sleeepyjack 185d3c4
Use std::abs
sleeepyjack 023d080
Use std::vector instead of thrust::host_vector>
sleeepyjack 53cdf37
Add note about shmem alignment
sleeepyjack 2a81714
Remove comment
sleeepyjack d859b39
Remove device-sided error handling since it hurts performance
sleeepyjack 43be0f0
Constexpr all the things!
sleeepyjack fbd6dab
Add constructor overload which takes the desired standard deviation
sleeepyjack dfe1a07
Remove stray include
sleeepyjack 2629adc
Bugfixes
sleeepyjack 1ad97e2
Fix merge
sleeepyjack 3b0da20
Add Spark parity tests
sleeepyjack f80509f
Fix error calculation
sleeepyjack bbb7258
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack b61a2db
Move include/cuco/sentinel.cuh -> include/cuco/types.cuh
sleeepyjack 9b0ee68
Move HLL-related strong types to types.cuh
sleeepyjack 75cd967
Apparently Doxygen has become even pickier...
sleeepyjack 9436931
Merge remote-tracking branch 'upstream/dev' into hll
sleeepyjack 6929f65
Clean up device ref example
sleeepyjack a496f9e
Update godbolt links
sleeepyjack 7fecd7b
Clean up unique sequence unit test
sleeepyjack File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| 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)) / | ||
|
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); | ||
| } | ||
106 changes: 106 additions & 0 deletions
106
include/cuco/detail/distinct_count_estimator/distinct_count_estimator.inl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
57 changes: 57 additions & 0 deletions
57
include/cuco/detail/distinct_count_estimator/distinct_count_estimator_ref.inl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.