-
Notifications
You must be signed in to change notification settings - Fork 912
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
Add distinct key inner join #14990
Add distinct key inner join #14990
Changes from all commits
5676a7e
f732c9c
f08fbee
17dc00f
03f3bb7
3fe41ef
4667bfa
156a2d5
8526494
35f561c
4cd9a6c
cf99ef0
e893061
08eeecb
5aa1a97
83c3d2f
30a9dd1
a049b8e
c55c42b
9e3a708
0f2c854
c2d572d
f59234d
ce9b546
4c9a391
5e7f2a5
67c683b
b3cd8c7
a451cb4
1854315
ddd4eaf
5d6821f
a9c5529
41876be
c1b583f
52f8c7e
a218567
0ad3bdb
5a435af
cd9dca3
ee2844c
e210b9e
5bebd9e
51a8c26
40b4b4c
6cb38c7
cc5a88e
9b9600a
99b3255
2ee3c40
5aebff1
1ba7015
119a531
26f867a
876ed60
4201de6
e6c1271
de2a20d
ae5c73a
7864c3d
3e8b8fb
30724e9
6cc98f5
a56ff0c
715a101
73700d9
35253e8
35f2d9d
f6f66cf
800bee7
1259b2d
7a169e2
2c6b439
a801cd3
bc8ca49
ce7d8c0
505a6ab
4b376de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* 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 "join_common.hpp" | ||
|
||
template <typename key_type, typename payload_type, bool Nullable> | ||
void distinct_inner_join(nvbench::state& state, | ||
nvbench::type_list<key_type, payload_type, nvbench::enum_type<Nullable>>) | ||
{ | ||
skip_helper(state); | ||
|
||
auto join = [](cudf::table_view const& left_input, | ||
cudf::table_view const& right_input, | ||
cudf::null_equality compare_nulls, | ||
rmm::cuda_stream_view stream) { | ||
auto const has_nulls = cudf::has_nested_nulls(left_input) || cudf::has_nested_nulls(right_input) | ||
? cudf::nullable_join::YES | ||
: cudf::nullable_join::NO; | ||
auto hj_obj = cudf::distinct_hash_join<cudf::has_nested::NO>{ | ||
left_input, right_input, has_nulls, compare_nulls, stream}; | ||
return hj_obj.inner_join(stream); | ||
}; | ||
|
||
BM_join<key_type, payload_type, Nullable>(state, join); | ||
} | ||
|
||
// inner join ----------------------------------------------------------------------- | ||
NVBENCH_BENCH_TYPES(distinct_inner_join, | ||
NVBENCH_TYPE_AXES(nvbench::type_list<nvbench::int32_t>, | ||
nvbench::type_list<nvbench::int32_t>, | ||
nvbench::enum_type_list<false>)) | ||
.set_name("distinct_inner_join_32bit") | ||
.set_type_axes_names({"Key Type", "Payload Type", "Nullable"}) | ||
.add_int64_axis("Build Table Size", {100'000, 10'000'000, 80'000'000, 100'000'000}) | ||
.add_int64_axis("Probe Table Size", | ||
{100'000, 400'000, 10'000'000, 40'000'000, 100'000'000, 240'000'000}); | ||
|
||
NVBENCH_BENCH_TYPES(distinct_inner_join, | ||
NVBENCH_TYPE_AXES(nvbench::type_list<nvbench::int64_t>, | ||
nvbench::type_list<nvbench::int64_t>, | ||
nvbench::enum_type_list<false>)) | ||
.set_name("distinct_inner_join_64bit") | ||
.set_type_axes_names({"Key Type", "Payload Type", "Nullable"}) | ||
.add_int64_axis("Build Table Size", {40'000'000, 50'000'000}) | ||
.add_int64_axis("Probe Table Size", {50'000'000, 120'000'000}); | ||
|
||
NVBENCH_BENCH_TYPES(distinct_inner_join, | ||
NVBENCH_TYPE_AXES(nvbench::type_list<nvbench::int32_t>, | ||
nvbench::type_list<nvbench::int32_t>, | ||
nvbench::enum_type_list<true>)) | ||
.set_name("distinct_inner_join_32bit_nulls") | ||
.set_type_axes_names({"Key Type", "Payload Type", "Nullable"}) | ||
.add_int64_axis("Build Table Size", {100'000, 10'000'000, 80'000'000, 100'000'000}) | ||
.add_int64_axis("Probe Table Size", | ||
{100'000, 400'000, 10'000'000, 40'000'000, 100'000'000, 240'000'000}); | ||
|
||
NVBENCH_BENCH_TYPES(distinct_inner_join, | ||
NVBENCH_TYPE_AXES(nvbench::type_list<nvbench::int64_t>, | ||
nvbench::type_list<nvbench::int64_t>, | ||
nvbench::enum_type_list<true>)) | ||
.set_name("distinct_inner_join_64bit_nulls") | ||
.set_type_axes_names({"Key Type", "Payload Type", "Nullable"}) | ||
.add_int64_axis("Build Table Size", {40'000'000, 50'000'000}) | ||
.add_int64_axis("Probe Table Size", {50'000'000, 120'000'000}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* | ||
* 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 <cudf/hashing/detail/helper_functions.cuh> | ||
#include <cudf/table/experimental/row_operators.cuh> | ||
#include <cudf/types.hpp> | ||
|
||
#include <rmm/cuda_stream_view.hpp> | ||
#include <rmm/device_uvector.hpp> | ||
|
||
#include <cuco/static_set.cuh> | ||
|
||
#include <cstddef> | ||
#include <memory> | ||
#include <type_traits> | ||
#include <utility> | ||
|
||
namespace cudf::detail { | ||
|
||
using cudf::experimental::row::lhs_index_type; | ||
using cudf::experimental::row::rhs_index_type; | ||
|
||
/** | ||
* @brief An comparator adapter wrapping both self comparator and two table comparator | ||
*/ | ||
template <typename Equal> | ||
struct comparator_adapter { | ||
Comment on lines
+37
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sound extremely familiar to me 😛 . Can you extract the code into some common header (or There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Such adapters look similar to each other while the contents are quite different, e.g. the insert comparator always returns false since we know all insert elements are distinct, the set key type is a pair of row hash value and row index which is rarely seen besides join operations, etc. |
||
comparator_adapter(Equal const& d_equal) : _d_equal{d_equal} {} | ||
|
||
__device__ constexpr auto operator()( | ||
cuco::pair<hash_value_type, lhs_index_type> const&, | ||
cuco::pair<hash_value_type, lhs_index_type> const&) const noexcept | ||
{ | ||
// All build table keys are distinct thus `false` no matter what | ||
return false; | ||
} | ||
|
||
__device__ constexpr auto operator()( | ||
cuco::pair<hash_value_type, lhs_index_type> const& lhs, | ||
cuco::pair<hash_value_type, rhs_index_type> const& rhs) const noexcept | ||
{ | ||
if (lhs.first != rhs.first) { return false; } | ||
return _d_equal(lhs.second, rhs.second); | ||
} | ||
|
||
private: | ||
Equal _d_equal; | ||
}; | ||
|
||
template <typename Hasher> | ||
struct hasher_adapter { | ||
hasher_adapter(Hasher const& d_hasher = {}) : _d_hasher{d_hasher} {} | ||
|
||
template <typename T> | ||
__device__ constexpr auto operator()(cuco::pair<hash_value_type, T> const& key) const noexcept | ||
{ | ||
return _d_hasher(key.first); | ||
} | ||
|
||
private: | ||
Hasher _d_hasher; | ||
}; | ||
|
||
/** | ||
* @brief Distinct hash join that builds hash table in creation and probes results in subsequent | ||
* `*_join` member functions. | ||
* | ||
* @tparam HasNested Flag indicating whether there are nested columns in build/probe table | ||
*/ | ||
template <cudf::has_nested HasNested> | ||
struct distinct_hash_join { | ||
private: | ||
/// Row equality type for nested columns | ||
using nested_row_equal = cudf::experimental::row::equality::strong_index_comparator_adapter< | ||
cudf::experimental::row::equality::device_row_comparator<true, cudf::nullate::DYNAMIC>>; | ||
/// Row equality type for flat columns | ||
using flat_row_equal = cudf::experimental::row::equality::strong_index_comparator_adapter< | ||
cudf::experimental::row::equality::device_row_comparator<false, cudf::nullate::DYNAMIC>>; | ||
|
||
/// Device row equal type | ||
using d_equal_type = | ||
std::conditional_t<HasNested == cudf::has_nested::YES, nested_row_equal, flat_row_equal>; | ||
using hasher = hasher_adapter<thrust::identity<hash_value_type>>; | ||
using probing_scheme_type = cuco::linear_probing<1, hasher>; | ||
using cuco_storage_type = cuco::storage<1>; | ||
|
||
/// Hash table type | ||
using hash_table_type = cuco::static_set<cuco::pair<hash_value_type, lhs_index_type>, | ||
cuco::extent<size_type>, | ||
cuda::thread_scope_device, | ||
comparator_adapter<d_equal_type>, | ||
probing_scheme_type, | ||
cudf::detail::cuco_allocator, | ||
cuco_storage_type>; | ||
|
||
bool _has_nulls; ///< true if nulls are present in either build table or probe table | ||
cudf::null_equality _nulls_equal; ///< whether to consider nulls as equal | ||
cudf::table_view _build; ///< input table to build the hash map | ||
cudf::table_view _probe; ///< input table to probe the hash map | ||
std::shared_ptr<cudf::experimental::row::equality::preprocessed_table> | ||
_preprocessed_build; ///< input table preprocssed for row operators | ||
std::shared_ptr<cudf::experimental::row::equality::preprocessed_table> | ||
_preprocessed_probe; ///< input table preprocssed for row operators | ||
hash_table_type _hash_table; ///< hash table built on `_build` | ||
|
||
public: | ||
distinct_hash_join() = delete; | ||
~distinct_hash_join() = default; | ||
distinct_hash_join(distinct_hash_join const&) = delete; | ||
distinct_hash_join(distinct_hash_join&&) = delete; | ||
distinct_hash_join& operator=(distinct_hash_join const&) = delete; | ||
distinct_hash_join& operator=(distinct_hash_join&&) = delete; | ||
|
||
/** | ||
* @brief Constructor that internally builds the hash table based on the given `build` table. | ||
* | ||
* @throw cudf::logic_error if the number of columns in `build` table is 0. | ||
* | ||
* @param build The build table, from which the hash table is built | ||
* @param probe The probe table | ||
* @param has_nulls Flag to indicate if any nulls exist in the `build` table or | ||
* any `probe` table that will be used later for join. | ||
* @param compare_nulls Controls whether null join-key values should match or not. | ||
* @param stream CUDA stream used for device memory operations and kernel launches. | ||
*/ | ||
distinct_hash_join(cudf::table_view const& build, | ||
cudf::table_view const& probe, | ||
bool has_nulls, | ||
cudf::null_equality compare_nulls, | ||
rmm::cuda_stream_view stream); | ||
|
||
/** | ||
* @copydoc cudf::distinct_hash_join::inner_join | ||
*/ | ||
std::pair<std::unique_ptr<rmm::device_uvector<size_type>>, | ||
std::unique_ptr<rmm::device_uvector<size_type>>> | ||
inner_join(rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr) const; | ||
}; | ||
} // namespace cudf::detail |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (c) 2019-2023, NVIDIA CORPORATION. | ||
* Copyright (c) 2019-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. | ||
|
@@ -33,6 +33,13 @@ | |
|
||
namespace cudf { | ||
|
||
/** | ||
* @brief Enum to indicate whether the distinct join table has nested columns or not | ||
* | ||
* @ingroup column_join | ||
*/ | ||
enum class has_nested : bool { YES, NO }; | ||
|
||
// forward declaration | ||
namespace hashing::detail { | ||
template <typename T> | ||
|
@@ -41,6 +48,9 @@ class MurmurHash3_x86_32; | |
namespace detail { | ||
template <typename T> | ||
class hash_join; | ||
|
||
template <cudf::has_nested HasNested> | ||
class distinct_hash_join; | ||
} // namespace detail | ||
|
||
/** | ||
|
@@ -438,6 +448,64 @@ class hash_join { | |
const std::unique_ptr<impl_type const> _impl; | ||
}; | ||
|
||
/** | ||
* @brief Distinct hash join that builds hash table in creation and probes results in subsequent | ||
* `*_join` member functions | ||
* | ||
* @note Behavior is undefined if the build table contains duplicates. | ||
* @note All NaNs are considered as equal | ||
* | ||
* @tparam HasNested Flag indicating whether there are nested columns in build/probe table | ||
*/ | ||
// TODO: `HasNested` to be removed via dispatching | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this TODO need to be addressed in this PR or a later PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably a separate PR. None of my current solutions is good enough thus need more time to think about this. |
||
template <cudf::has_nested HasNested> | ||
class distinct_hash_join { | ||
public: | ||
distinct_hash_join() = delete; | ||
~distinct_hash_join(); | ||
distinct_hash_join(distinct_hash_join const&) = delete; | ||
distinct_hash_join(distinct_hash_join&&) = delete; | ||
distinct_hash_join& operator=(distinct_hash_join const&) = delete; | ||
distinct_hash_join& operator=(distinct_hash_join&&) = delete; | ||
|
||
/** | ||
* @brief Constructs a distinct hash join object for subsequent probe calls | ||
* | ||
* @param build The build table that contains distinct elements | ||
* @param probe The probe table, from which the keys are probed | ||
* @param has_nulls Flag to indicate if there exists any nulls in the `build` table or | ||
* any `probe` table that will be used later for join | ||
* @param compare_nulls Controls whether null join-key values should match or not | ||
* @param stream CUDA stream used for device memory operations and kernel launches | ||
*/ | ||
distinct_hash_join(cudf::table_view const& build, | ||
cudf::table_view const& probe, | ||
nullable_join has_nulls = nullable_join::YES, | ||
null_equality compare_nulls = null_equality::EQUAL, | ||
rmm::cuda_stream_view stream = cudf::get_default_stream()); | ||
|
||
/** | ||
* Returns the row indices that can be used to construct the result of performing | ||
* an inner join between two tables. @see cudf::inner_join(). | ||
* | ||
* @param stream CUDA stream used for device memory operations and kernel launches | ||
* @param mr Device memory resource used to allocate the returned indices' device memory. | ||
* | ||
* @return A pair of columns [`build_indices`, `probe_indices`] that can be used to construct | ||
* the result of performing an inner join between two tables with `build` and `probe` | ||
* as the join keys. | ||
*/ | ||
std::pair<std::unique_ptr<rmm::device_uvector<size_type>>, | ||
std::unique_ptr<rmm::device_uvector<size_type>>> | ||
inner_join(rmm::cuda_stream_view stream = cudf::get_default_stream(), | ||
rmm::mr::device_memory_resource* mr = rmm::mr::get_current_device_resource()) const; | ||
|
||
private: | ||
using impl_type = typename cudf::detail::distinct_hash_join<HasNested>; ///< Implementation type | ||
|
||
std::unique_ptr<impl_type> _impl; ///< Distinct hash join implementation | ||
}; | ||
|
||
/** | ||
* @brief Returns a pair of row index vectors corresponding to all pairs | ||
* of rows between the specified tables where the predicate evaluates to true. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉