From ff1bc7e290508f2990af097f019cfecc96298093 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 2 Mar 2023 15:49:44 -0800 Subject: [PATCH 001/140] Add tests Signed-off-by: Nghia Truong --- cpp/tests/CMakeLists.txt | 3 +- cpp/tests/sort/nested_types_tests.cpp | 78 +++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 cpp/tests/sort/nested_types_tests.cpp diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 13cb1739ae9..66c57a827b7 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -243,8 +243,7 @@ endif() # ################################################################################################## # * sort tests ------------------------------------------------------------------------------------ ConfigureTest( - SORT_TEST sort/segmented_sort_tests.cpp sort/sort_test.cpp sort/stable_sort_tests.cpp - sort/rank_test.cpp + SORT_TEST sort/nested_types_tests.cpp ) # ################################################################################################## diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp new file mode 100644 index 00000000000..8792d8259e8 --- /dev/null +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021-2023, 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 +#include +#include +#include + +#include + +using int32s_lists = cudf::test::lists_column_wrapper; +using int32s_col = cudf::test::fixed_width_column_wrapper; +using structs_col = cudf::test::structs_column_wrapper; + +struct structs_test : public cudf::test::BaseFixture { +}; + +TEST_F(structs_test, StructsHaveLists) +{ + auto const input = [] { + auto child1 = int32s_lists{{1, 1}, {2, 2}, {3, 3}, {1, 1}, {2, 2}}; + auto child2 = int32s_col{1, 2, 3, 1, 2}; + return structs_col{{child1, child2}}; + }(); + + printf("line %d\n", __LINE__); + cudf::test::print(input); + + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + + printf("line %d\n", __LINE__); + cudf::test::print(*order); + + auto const sorted = cudf::sort(cudf::table_view{{input}}); + + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); +} + +TEST_F(structs_test, ListsHaveStructs) +{ + auto const input = [] { + auto const get_structs = [] { + auto child1 = int32s_col{1, 2, 3, 0, 1, 2, 4, 5, 0, 1}; + auto child2 = int32s_col{11, 12, 13, 10, 11, 12, 14, 15, 10, 11}; + return structs_col{{child1, child2}}; + }; + + return cudf::make_lists_column( + 4, int32s_col{0, 3, 6, 8, 10}.release(), get_structs().release(), 0, {}); + }(); + + printf("line %d\n", __LINE__); + cudf::test::print(*input); + + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + + printf("line %d\n", __LINE__); + cudf::test::print(*order); + + auto const sorted = cudf::sort(cudf::table_view{{*input}}); + + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); +} From b02abaec6370a35d62d89edd7ea3b97564d96010 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 2 Mar 2023 16:28:51 -0800 Subject: [PATCH 002/140] Complete tests Signed-off-by: Nghia Truong --- cpp/tests/sort/nested_types_tests.cpp | 90 +++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 12 deletions(-) diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp index 8792d8259e8..6f324bdcfc8 100644 --- a/cpp/tests/sort/nested_types_tests.cpp +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -19,12 +19,46 @@ #include #include +#include #include using int32s_lists = cudf::test::lists_column_wrapper; using int32s_col = cudf::test::fixed_width_column_wrapper; using structs_col = cudf::test::structs_column_wrapper; +namespace { +std::vector flatten_lists_of_structs(cudf::column_view const& input) +{ + if (input.type().id() != cudf::type_id::LIST || + input.child(cudf::lists_column_view::child_column_index).type().id() != + cudf::type_id::STRUCT) { + return {input}; + } + + // TODO: Deal with sliced input. + CUDF_EXPECTS(input.offset() == 0, "Sliced input is not yet supported."); + + auto const offsets = input.child(cudf::lists_column_view::offsets_column_index); + auto const children = input.child(cudf::lists_column_view::child_column_index); + std::vector output; + + for (auto it = children.child_begin(); it != children.child_end(); ++it) { + auto const new_column = cudf::column_view{cudf::data_type{cudf::type_id::LIST}, + input.size(), + nullptr, + input.null_mask(), + input.null_count(), + 0, + {offsets, *it}}; + // The new column may still be lists of structs, thus we recursively call this: + auto const flattened_new_column = flatten_lists_of_structs(new_column); + output.insert(output.end(), flattened_new_column.begin(), flattened_new_column.end()); + } + + return output; +} +} // namespace + struct structs_test : public cudf::test::BaseFixture { }; @@ -39,15 +73,31 @@ TEST_F(structs_test, StructsHaveLists) printf("line %d\n", __LINE__); cudf::test::print(input); - auto const order = cudf::sorted_order(cudf::table_view{{input}}); + { + auto const order = cudf::sorted_order(cudf::table_view{{input}}); - printf("line %d\n", __LINE__); - cudf::test::print(*order); + printf("line %d\n", __LINE__); + cudf::test::print(*order); - auto const sorted = cudf::sort(cudf::table_view{{input}}); + auto const sorted = cudf::sort(cudf::table_view{{input}}); - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); + } + + { + auto const flattened = + cudf::structs::detail::flatten_nested_columns(cudf::table_view{{input}}, {}, {}); + auto const order = cudf::sorted_order(flattened.flattened_columns()); + + printf("line %d\n", __LINE__); + cudf::test::print(*order); + + auto const sorted = cudf::sort(cudf::table_view{{flattened.flattened_columns()}}); + + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); + } } TEST_F(structs_test, ListsHaveStructs) @@ -66,13 +116,29 @@ TEST_F(structs_test, ListsHaveStructs) printf("line %d\n", __LINE__); cudf::test::print(*input); - auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + { + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); - printf("line %d\n", __LINE__); - cudf::test::print(*order); + printf("line %d\n", __LINE__); + cudf::test::print(*order); - auto const sorted = cudf::sort(cudf::table_view{{*input}}); + auto const sorted = cudf::sort(cudf::table_view{{*input}}); - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); + } + + { + auto const flattened = flatten_lists_of_structs(*input); + + auto const order = cudf::sorted_order(cudf::table_view{flattened}); + + printf("line %d\n", __LINE__); + cudf::test::print(*order); + + auto const sorted = cudf::sort(cudf::table_view{flattened}); + + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); + } } From 3f6f2f3590607e2b333dfd4bf04a12ae44ac91c3 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 2 Mar 2023 16:29:05 -0800 Subject: [PATCH 003/140] Disable unsupported conditions Signed-off-by: Nghia Truong --- cpp/src/structs/utilities.cpp | 4 ++-- cpp/src/table/row_operators.cu | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/src/structs/utilities.cpp b/cpp/src/structs/utilities.cpp index 6808ed4ea2e..0c811904e29 100644 --- a/cpp/src/structs/utilities.cpp +++ b/cpp/src/structs/utilities.cpp @@ -121,8 +121,8 @@ struct table_flattener { void fail_if_unsupported_types(table_view const& input) const { - auto const has_lists = std::any_of(input.begin(), input.end(), is_or_has_nested_lists); - CUDF_EXPECTS(not has_lists, "Flattening LIST columns is not supported."); + // auto const has_lists = std::any_of(input.begin(), input.end(), is_or_has_nested_lists); + // CUDF_EXPECTS(not has_lists, "Flattening LIST columns is not supported."); } // Convert null_mask to BOOL8 columns and flatten the struct children in order. diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 8a63a6f6411..2ee7e5787c1 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -285,13 +285,13 @@ void check_lex_compatibility(table_view const& input) column_checker_fn_t check_column = [&](column_view const& c) { if (c.type().id() == type_id::LIST) { auto const& list_col = lists_column_view(c); - CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, - "Cannot lexicographic compare a table with a LIST of STRUCT column"); + // CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, + // "Cannot lexicographic compare a table with a LIST of STRUCT column"); check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { - CUDF_EXPECTS(child->type().id() != type_id::LIST, - "Cannot lexicographic compare a table with a STRUCT of LIST column"); + // CUDF_EXPECTS(child->type().id() != type_id::LIST, + // "Cannot lexicographic compare a table with a STRUCT of LIST column"); check_column(*child); } } From 3609edfb4ae3cf432b9937a397fec5a1e98b0414 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 2 Mar 2023 22:23:52 -0800 Subject: [PATCH 004/140] Reverse `row_operator.cu` Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 2ee7e5787c1..8a63a6f6411 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -285,13 +285,13 @@ void check_lex_compatibility(table_view const& input) column_checker_fn_t check_column = [&](column_view const& c) { if (c.type().id() == type_id::LIST) { auto const& list_col = lists_column_view(c); - // CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, - // "Cannot lexicographic compare a table with a LIST of STRUCT column"); + CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, + "Cannot lexicographic compare a table with a LIST of STRUCT column"); check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { - // CUDF_EXPECTS(child->type().id() != type_id::LIST, - // "Cannot lexicographic compare a table with a STRUCT of LIST column"); + CUDF_EXPECTS(child->type().id() != type_id::LIST, + "Cannot lexicographic compare a table with a STRUCT of LIST column"); check_column(*child); } } From 7aede425725aa4952d2abbb76a07cf56398d3a79 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 2 Mar 2023 22:24:30 -0800 Subject: [PATCH 005/140] Revert "Reverse `row_operator.cu`" This reverts commit 3609edfb4ae3cf432b9937a397fec5a1e98b0414. --- cpp/src/table/row_operators.cu | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 8a63a6f6411..2ee7e5787c1 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -285,13 +285,13 @@ void check_lex_compatibility(table_view const& input) column_checker_fn_t check_column = [&](column_view const& c) { if (c.type().id() == type_id::LIST) { auto const& list_col = lists_column_view(c); - CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, - "Cannot lexicographic compare a table with a LIST of STRUCT column"); + // CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, + // "Cannot lexicographic compare a table with a LIST of STRUCT column"); check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { - CUDF_EXPECTS(child->type().id() != type_id::LIST, - "Cannot lexicographic compare a table with a STRUCT of LIST column"); + // CUDF_EXPECTS(child->type().id() != type_id::LIST, + // "Cannot lexicographic compare a table with a STRUCT of LIST column"); check_column(*child); } } From 452f1b1279d22c8ce5eb756f53c23dc75a99ad5c Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 2 Mar 2023 22:25:14 -0800 Subject: [PATCH 006/140] Update tests Signed-off-by: Nghia Truong --- cpp/tests/sort/nested_types_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp index 6f324bdcfc8..0542b513905 100644 --- a/cpp/tests/sort/nested_types_tests.cpp +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -73,7 +73,7 @@ TEST_F(structs_test, StructsHaveLists) printf("line %d\n", __LINE__); cudf::test::print(input); - { + if (1) { auto const order = cudf::sorted_order(cudf::table_view{{input}}); printf("line %d\n", __LINE__); @@ -116,7 +116,7 @@ TEST_F(structs_test, ListsHaveStructs) printf("line %d\n", __LINE__); cudf::test::print(*input); - { + if (1) { auto const order = cudf::sorted_order(cudf::table_view{{*input}}); printf("line %d\n", __LINE__); From ce8f088ddedae5d71add65bd9c08ac604d0982d2 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 07:54:28 -0800 Subject: [PATCH 007/140] Fix offset Signed-off-by: Nghia Truong --- cpp/tests/sort/nested_types_tests.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp index 0542b513905..23001e9ec52 100644 --- a/cpp/tests/sort/nested_types_tests.cpp +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -35,9 +35,6 @@ std::vector flatten_lists_of_structs(cudf::column_view const& return {input}; } - // TODO: Deal with sliced input. - CUDF_EXPECTS(input.offset() == 0, "Sliced input is not yet supported."); - auto const offsets = input.child(cudf::lists_column_view::offsets_column_index); auto const children = input.child(cudf::lists_column_view::child_column_index); std::vector output; @@ -48,7 +45,7 @@ std::vector flatten_lists_of_structs(cudf::column_view const& nullptr, input.null_mask(), input.null_count(), - 0, + input.offset(), {offsets, *it}}; // The new column may still be lists of structs, thus we recursively call this: auto const flattened_new_column = flatten_lists_of_structs(new_column); From 799f2ae6dbc13954e069d83266641b63b36c79b6 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 14:29:59 -0800 Subject: [PATCH 008/140] Change return type to `unique_ptr` --- cpp/include/cudf/detail/structs/utilities.hpp | 2 +- cpp/src/structs/utilities.cpp | 27 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cpp/include/cudf/detail/structs/utilities.hpp b/cpp/include/cudf/detail/structs/utilities.hpp index c8b758ca337..70975d73242 100644 --- a/cpp/include/cudf/detail/structs/utilities.hpp +++ b/cpp/include/cudf/detail/structs/utilities.hpp @@ -167,7 +167,7 @@ class flattened_table { * @return `flatten_result` with flattened table, flattened column order, flattened null precedence, * alongside the supporting columns and device_buffers for the flattened table. */ -[[nodiscard]] flattened_table flatten_nested_columns( +[[nodiscard]] std::unique_ptr flatten_nested_columns( table_view const& input, std::vector const& column_order, std::vector const& null_precedence, diff --git a/cpp/src/structs/utilities.cpp b/cpp/src/structs/utilities.cpp index 6808ed4ea2e..34a0e75a942 100644 --- a/cpp/src/structs/utilities.cpp +++ b/cpp/src/structs/utilities.cpp @@ -186,21 +186,28 @@ struct table_flattener { } } - return flattened_table{table_view{flat_columns}, - std::move(flat_column_order), - std::move(flat_null_precedence), - std::move(validity_as_column), - std::move(nullable_data)}; + return std::make_unique(table_view{flat_columns}, + std::move(flat_column_order), + std::move(flat_null_precedence), + std::move(validity_as_column), + std::move(nullable_data)); } }; -flattened_table flatten_nested_columns(table_view const& input, - std::vector const& column_order, - std::vector const& null_precedence, - column_nullability nullability) +std::unique_ptr flatten_nested_columns( + table_view const& input, + std::vector const& column_order, + std::vector const& null_precedence, + column_nullability nullability) { auto const has_struct = std::any_of(input.begin(), input.end(), is_struct); - if (not has_struct) { return flattened_table{input, column_order, null_precedence, {}, {}}; } + if (not has_struct) { + return std::make_unique(input, + column_order, + null_precedence, + std::vector>{}, + temporary_nullable_data{}); + } return table_flattener{input, column_order, null_precedence, nullability}(); } From de7437ebbd147fb7c449d9dc33bd5913f47339a2 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 14:30:05 -0800 Subject: [PATCH 009/140] Adapt with changes --- cpp/include/cudf/detail/join.hpp | 7 +++++-- cpp/src/join/hash_join.cu | 10 +++++----- cpp/src/reductions/struct_minmax_util.cuh | 7 ++++--- cpp/src/search/contains_table.cu | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/cpp/include/cudf/detail/join.hpp b/cpp/include/cudf/detail/join.hpp index 2dfe31091ac..af5f26fb508 100644 --- a/cpp/include/cudf/detail/join.hpp +++ b/cpp/include/cudf/detail/join.hpp @@ -16,7 +16,6 @@ #pragma once #include -#include #include #include #include @@ -37,6 +36,10 @@ template class default_allocator; +namespace cudf::structs::detail { +class flattened_table; +} + namespace cudf { namespace detail { @@ -74,7 +77,7 @@ struct hash_join { rmm::device_buffer const _composite_bitmask; ///< Bitmask to denote whether a row is valid cudf::null_equality const _nulls_equal; ///< whether to consider nulls as equal cudf::table_view _build; ///< input table to build the hash map - cudf::structs::detail::flattened_table + std::unique_ptr _flattened_build_table; ///< flattened data structures for `_build` map_type _hash_table; ///< hash table built on `_build` diff --git a/cpp/src/join/hash_join.cu b/cpp/src/join/hash_join.cu index cce917a24de..87df422a264 100644 --- a/cpp/src/join/hash_join.cu +++ b/cpp/src/join/hash_join.cu @@ -300,7 +300,7 @@ hash_join::hash_join(cudf::table_view const& build, // need to store off the owning structures for some of the views in _build _flattened_build_table = structs::detail::flatten_nested_columns( build, {}, {}, structs::detail::column_nullability::FORCE); - _build = _flattened_build_table; + _build = _flattened_build_table->flattened_columns(); if (_is_empty) { return; } @@ -358,7 +358,7 @@ std::size_t hash_join::inner_join_size(cudf::table_view const& probe, auto flattened_probe = structs::detail::flatten_nested_columns( probe, {}, {}, structs::detail::column_nullability::FORCE); - auto const flattened_probe_table = flattened_probe.flattened_columns(); + auto const flattened_probe_table = flattened_probe->flattened_columns(); auto build_table_ptr = cudf::table_device_view::create(_build, stream); auto flattened_probe_table_ptr = cudf::table_device_view::create(flattened_probe_table, stream); @@ -383,7 +383,7 @@ std::size_t hash_join::left_join_size(cudf::table_view const& probe, auto flattened_probe = structs::detail::flatten_nested_columns( probe, {}, {}, structs::detail::column_nullability::FORCE); - auto const flattened_probe_table = flattened_probe.flattened_columns(); + auto const flattened_probe_table = flattened_probe->flattened_columns(); auto build_table_ptr = cudf::table_device_view::create(_build, stream); auto flattened_probe_table_ptr = cudf::table_device_view::create(flattened_probe_table, stream); @@ -409,7 +409,7 @@ std::size_t hash_join::full_join_size(cudf::table_view const& probe, auto flattened_probe = structs::detail::flatten_nested_columns( probe, {}, {}, structs::detail::column_nullability::FORCE); - auto const flattened_probe_table = flattened_probe.flattened_columns(); + auto const flattened_probe_table = flattened_probe->flattened_columns(); auto build_table_ptr = cudf::table_device_view::create(_build, stream); auto flattened_probe_table_ptr = cudf::table_device_view::create(flattened_probe_table, stream); @@ -476,7 +476,7 @@ hash_join::compute_hash_join(cudf::table_view const& probe, auto flattened_probe = structs::detail::flatten_nested_columns( probe, {}, {}, structs::detail::column_nullability::FORCE); - auto const flattened_probe_table = flattened_probe.flattened_columns(); + auto const flattened_probe_table = flattened_probe->flattened_columns(); CUDF_EXPECTS(_build.num_columns() == flattened_probe_table.num_columns(), "Mismatch in number of columns to be joined on"); diff --git a/cpp/src/reductions/struct_minmax_util.cuh b/cpp/src/reductions/struct_minmax_util.cuh index a25d78d162a..ccbdcc5a684 100644 --- a/cpp/src/reductions/struct_minmax_util.cuh +++ b/cpp/src/reductions/struct_minmax_util.cuh @@ -87,7 +87,7 @@ auto static constexpr DEFAULT_NULL_ORDER = cudf::null_order::BEFORE; */ class comparison_binop_generator { private: - cudf::structs::detail::flattened_table const flattened_input; + std::unique_ptr const flattened_input; std::unique_ptr> const d_flattened_input_ptr; bool const is_min_op; @@ -99,13 +99,14 @@ class comparison_binop_generator { comparison_binop_generator(column_view const& input, rmm::cuda_stream_view stream, bool is_min_op) : flattened_input{cudf::structs::detail::flatten_nested_columns( table_view{{input}}, {}, std::vector{DEFAULT_NULL_ORDER})}, - d_flattened_input_ptr{table_device_view::create(flattened_input, stream)}, + d_flattened_input_ptr{ + table_device_view::create(flattened_input->flattened_columns(), stream)}, is_min_op(is_min_op), has_nulls{has_nested_nulls(table_view{{input}})}, null_orders_dvec(0, stream) { if (is_min_op) { - null_orders = flattened_input.null_orders(); + null_orders = flattened_input->null_orders(); // If the input column has nulls (at the top level), null structs are excluded from the // operations, and that is equivalent to considering top-level nulls as larger than all other // non-null STRUCT elements (if finding for ARGMIN), or smaller than all other non-null STRUCT diff --git a/cpp/src/search/contains_table.cu b/cpp/src/search/contains_table.cu index c1cc4659a19..4be2b182229 100644 --- a/cpp/src/search/contains_table.cu +++ b/cpp/src/search/contains_table.cu @@ -329,8 +329,8 @@ rmm::device_uvector contains_without_lists_or_nans(table_view const& hayst structs::detail::flatten_nested_columns(haystack, {}, {}, flatten_nullability); auto const needles_flattened_tables = structs::detail::flatten_nested_columns(needles, {}, {}, flatten_nullability); - auto const haystack_flattened = haystack_flattened_tables.flattened_columns(); - auto const needles_flattened = needles_flattened_tables.flattened_columns(); + auto const haystack_flattened = haystack_flattened_tables->flattened_columns(); + auto const needles_flattened = needles_flattened_tables->flattened_columns(); auto const haystack_tdv_ptr = table_device_view::create(haystack_flattened, stream); auto const needles_tdv_ptr = table_device_view::create(needles_flattened, stream); From 6cc4390378daa0a8a095baeb8def65ecfe79f9b0 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 14:41:10 -0800 Subject: [PATCH 010/140] Update copyright year --- cpp/include/cudf/detail/join.hpp | 2 +- cpp/include/cudf/detail/structs/utilities.hpp | 2 +- cpp/src/reductions/struct_minmax_util.cuh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/include/cudf/detail/join.hpp b/cpp/include/cudf/detail/join.hpp index af5f26fb508..e539b1a34c8 100644 --- a/cpp/include/cudf/detail/join.hpp +++ b/cpp/include/cudf/detail/join.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/include/cudf/detail/structs/utilities.hpp b/cpp/include/cudf/detail/structs/utilities.hpp index 70975d73242..90ae39fb2ce 100644 --- a/cpp/include/cudf/detail/structs/utilities.hpp +++ b/cpp/include/cudf/detail/structs/utilities.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. + * Copyright (c) 2020-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/src/reductions/struct_minmax_util.cuh b/cpp/src/reductions/struct_minmax_util.cuh index ccbdcc5a684..187baba00c1 100644 --- a/cpp/src/reductions/struct_minmax_util.cuh +++ b/cpp/src/reductions/struct_minmax_util.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 4092aae335ade44f5e442f70e20f99228e302560 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 14:45:57 -0800 Subject: [PATCH 011/140] Add more variable Signed-off-by: Nghia Truong --- .../cudf/table/experimental/row_operators.cuh | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 2a207d2a5c4..c8eedd7edeb 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -54,6 +54,11 @@ #include #include +// Forward declaration to reduce dependency on the relevant header. +namespace cudf::structs::detail { +class flattened_table; +} + namespace cudf { namespace experimental { @@ -721,30 +726,41 @@ struct preprocessed_table { * contain an empty `dremel_device_view`. As such, this uvector has as many elements as there are * columns in the table (unlike the `dremel_data` parameter, which is only as long as the number * of list columns). + * @param TODO */ - preprocessed_table(table_device_view_owner&& table, - rmm::device_uvector&& column_order, - rmm::device_uvector&& null_precedence, - rmm::device_uvector&& depths, - std::vector&& dremel_data, - rmm::device_uvector&& dremel_device_views) + preprocessed_table( + table_device_view_owner&& table, + rmm::device_uvector&& column_order, + rmm::device_uvector&& null_precedence, + rmm::device_uvector&& depths, + std::vector&& dremel_data, + rmm::device_uvector&& dremel_device_views, + std::unique_ptr&& flattened_input_aux_data) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data(std::move(dremel_data)), - _dremel_device_views(std::move(dremel_device_views)){}; + _dremel_device_views(std::move(dremel_device_views)), + _flattened_input_aux_data(std::move(flattened_input_aux_data)) + { + } - preprocessed_table(table_device_view_owner&& table, - rmm::device_uvector&& column_order, - rmm::device_uvector&& null_precedence, - rmm::device_uvector&& depths) + preprocessed_table( + table_device_view_owner&& table, + rmm::device_uvector&& column_order, + rmm::device_uvector&& null_precedence, + rmm::device_uvector&& depths, + std::unique_ptr&& flattened_input_aux_data) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data{}, - _dremel_device_views{} {}; + _dremel_device_views{}, + _flattened_input_aux_data(std::move(flattened_input_aux_data)) + { + } /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -811,6 +827,10 @@ struct preprocessed_table { // Dremel encoding of list columns used for the comparison algorithm std::optional> _dremel_data; std::optional> _dremel_device_views; + + // Auxiliary data generated from `cudf::structs::detail::flatten_nested_columns` + // that needs to be kept alive. + std::unique_ptr _flattened_input_aux_data; }; /** From bc9ecc4c97a2c712d7ff0daae8fcc75afd647b4c Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 14:48:17 -0800 Subject: [PATCH 012/140] Implement flattening Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 141 +++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 8 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 2ee7e5787c1..a889af7849b 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -344,23 +344,145 @@ namespace row { namespace lexicographic { +namespace { + +/** + * @brief has_mixed_lists_and_structs + * @param input + * @return + */ +// bool has_mixed_lists_and_structs(column_view const& input) +//{ +// auto const id = input.type().id(); + +// if (id == type_id::LIST) { +// auto const child = input.child(lists_column_view::child_column_index); +// return child.type().id() == type_id::STRUCT || has_mixed_lists_and_structs(child); +// } else if (id == type_id::STRUCT) { +// return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { +// return child.type().id() == type_id::LIST || has_mixed_lists_and_structs(child); +// }); +// } + +// return false; +//} + +/** + * @brief has_structs_of_lists + * @param input + * @return + */ +bool has_structs_of_lists(column_view const& input) +{ + if (input.type().id() == type_id::STRUCT) { + return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { + return child.type().id() == type_id::LIST || has_structs_of_lists(child); + }); + } else if (id == type_id::LIST) { + return has_structs_of_lists(input.child(lists_column_view::child_column_index)); + } + + return false; +} + +/** + * @brief has_structs_of_lists + * @param input + * @return + */ +// bool has_lists_of_structs(column_view const& input) +//{ +// if (input.type().id() == type_id::LIST) { +// return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { +// return child.type().id() == type_id::STRUCT || has_lists_of_structs(child); +// }); +// } + +// return false; +//} + +/** + * @brief flatten_lists + * @param input + * @return + */ +std::vector flatten_lists_column(column_view const& input) +{ + if (input.type().id() != type_id::LIST || + input.child(lists_column_view::child_column_index).type().id() != type_id::STRUCT) { + return {input}; + } + + auto const offsets = input.child(lists_column_view::offsets_column_index); + auto const children = input.child(lists_column_view::child_column_index); + std::vector output; + + for (auto it = children.child_begin(); it != children.child_end(); ++it) { + auto const new_column = column_view{data_type{type_id::LIST}, + input.size(), + nullptr, + input.null_mask(), + input.null_count(), + input.offset(), + {offsets, *it}}; + // The new column may still be lists of structs, thus we recursively call this: + auto const flattened_new_column = flatten_lists_column(new_column); + output.insert(output.end(), flattened_new_column.begin(), flattened_new_column.end()); + } + + return output; +} + +table_view flatten_lists(table_view const& input) +{ + std::vector out_cols; + for (auto it = input.begin(); it != input.end(); ++it) { + auto const flattened = flatten_lists_column(*it); + out_cols.insert(out_cols.end(), flattened.begin(), flattened.end()); + } + + return table_view{out_cols}; +} + +/** + * @brief flatten + * @param t + */ +std::pair> +flatten_nested_lists_or_structs(table_view const& input) +{ + // Firstly, extract lists of structs (if any) into multiple lists of primitive types. + auto lists_flattened = flatten_lists(input); + + if (has_structs_of_lists(input)) { + auto structs_flattened = cudf::structs::detail::flatten_nested_columns(lists_flattened, {}, {}); + auto output_table = flatten_lists(structs_flattened->flattened_columns()); + return {std::move(output_table), std::move(structs_flattened)}; + } + + return {std::move(lists_flattened), nullptr}; +} + +} // namespace + std::shared_ptr preprocessed_table::create( table_view const& t, host_span column_order, host_span null_precedence, rmm::cuda_stream_view stream) { - check_lex_compatibility(t); + auto [flattened_t, flattened_t_aux_data] = flatten_nested_lists_or_structs(t); + check_lex_compatibility(flattened_t); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = - decompose_structs(t, column_order, null_precedence); + decompose_structs(flattened_t, column_order, null_precedence); auto d_t = table_device_view::create(verticalized_lhs, stream); auto d_column_order = detail::make_device_uvector_async(new_column_order, stream); auto d_null_precedence = detail::make_device_uvector_async(new_null_precedence, stream); auto d_depths = detail::make_device_uvector_async(verticalized_col_depths, stream); - if (detail::has_nested_columns(t)) { + if (detail::has_nested_columns(flattened_t)) { auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(verticalized_lhs, stream); return std::shared_ptr( new preprocessed_table(std::move(d_t), @@ -368,12 +490,15 @@ std::shared_ptr preprocessed_table::create( std::move(d_null_precedence), std::move(d_depths), std::move(dremel_data), - std::move(d_dremel_device_view))); + std::move(d_dremel_device_view), + std::move(flattened_t_aux_data))); } else { - return std::shared_ptr(new preprocessed_table(std::move(d_t), - std::move(d_column_order), - std::move(d_null_precedence), - std::move(d_depths))); + return std::shared_ptr( + new preprocessed_table(std::move(d_t), + std::move(d_column_order), + std::move(d_null_precedence), + std::move(d_depths), + std::move(flattened_t_aux_data))); } } From 284a79c7e366e5f1681295481de55a2cc045a962 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 15:00:07 -0800 Subject: [PATCH 013/140] Complete implementation Signed-off-by: Nghia Truong --- .../cudf/table/experimental/row_operators.cuh | 22 +---------- cpp/src/table/row_operators.cu | 38 ++++++++++++++++++- cpp/tests/sort/nested_types_tests.cpp | 4 +- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index c8eedd7edeb..93be727a3e4 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -735,32 +735,14 @@ struct preprocessed_table { rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::unique_ptr&& flattened_input_aux_data) - : _t(std::move(table)), - _column_order(std::move(column_order)), - _null_precedence(std::move(null_precedence)), - _depths(std::move(depths)), - _dremel_data(std::move(dremel_data)), - _dremel_device_views(std::move(dremel_device_views)), - _flattened_input_aux_data(std::move(flattened_input_aux_data)) - { - } + std::unique_ptr&& flattened_input_aux_data); preprocessed_table( table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::unique_ptr&& flattened_input_aux_data) - : _t(std::move(table)), - _column_order(std::move(column_order)), - _null_precedence(std::move(null_precedence)), - _depths(std::move(depths)), - _dremel_data{}, - _dremel_device_views{}, - _flattened_input_aux_data(std::move(flattened_input_aux_data)) - { - } + std::unique_ptr&& flattened_input_aux_data); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index a889af7849b..f8bacd34aea 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -378,7 +378,7 @@ bool has_structs_of_lists(column_view const& input) return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { return child.type().id() == type_id::LIST || has_structs_of_lists(child); }); - } else if (id == type_id::LIST) { + } else if (input.type().id() == type_id::LIST) { return has_structs_of_lists(input.child(lists_column_view::child_column_index)); } @@ -454,7 +454,7 @@ flatten_nested_lists_or_structs(table_view const& input) // Firstly, extract lists of structs (if any) into multiple lists of primitive types. auto lists_flattened = flatten_lists(input); - if (has_structs_of_lists(input)) { + if (std::any_of(lists_flattened.begin(), lists_flattened.end(), has_structs_of_lists)) { auto structs_flattened = cudf::structs::detail::flatten_nested_columns(lists_flattened, {}, {}); auto output_table = flatten_lists(structs_flattened->flattened_columns()); return {std::move(output_table), std::move(structs_flattened)}; @@ -502,6 +502,40 @@ std::shared_ptr preprocessed_table::create( } } +preprocessed_table::preprocessed_table( + table_device_view_owner&& table, + rmm::device_uvector&& column_order, + rmm::device_uvector&& null_precedence, + rmm::device_uvector&& depths, + std::vector&& dremel_data, + rmm::device_uvector&& dremel_device_views, + std::unique_ptr&& flattened_input_aux_data) + : _t(std::move(table)), + _column_order(std::move(column_order)), + _null_precedence(std::move(null_precedence)), + _depths(std::move(depths)), + _dremel_data(std::move(dremel_data)), + _dremel_device_views(std::move(dremel_device_views)), + _flattened_input_aux_data(std::move(flattened_input_aux_data)) +{ +} + +preprocessed_table::preprocessed_table( + table_device_view_owner&& table, + rmm::device_uvector&& column_order, + rmm::device_uvector&& null_precedence, + rmm::device_uvector&& depths, + std::unique_ptr&& flattened_input_aux_data) + : _t(std::move(table)), + _column_order(std::move(column_order)), + _null_precedence(std::move(null_precedence)), + _depths(std::move(depths)), + _dremel_data{}, + _dremel_device_views{}, + _flattened_input_aux_data(std::move(flattened_input_aux_data)) +{ +} + two_table_comparator::two_table_comparator(table_view const& left, table_view const& right, host_span column_order, diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp index 23001e9ec52..371fc182107 100644 --- a/cpp/tests/sort/nested_types_tests.cpp +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -85,12 +85,12 @@ TEST_F(structs_test, StructsHaveLists) { auto const flattened = cudf::structs::detail::flatten_nested_columns(cudf::table_view{{input}}, {}, {}); - auto const order = cudf::sorted_order(flattened.flattened_columns()); + auto const order = cudf::sorted_order(flattened->flattened_columns()); printf("line %d\n", __LINE__); cudf::test::print(*order); - auto const sorted = cudf::sort(cudf::table_view{{flattened.flattened_columns()}}); + auto const sorted = cudf::sort(cudf::table_view{{flattened->flattened_columns()}}); printf("line %d\n", __LINE__); cudf::test::print(sorted->get_column(0).view()); From c11b6e59445d2ecf2ea56b30da03be233fa2a78e Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 15:00:18 -0800 Subject: [PATCH 014/140] Cleanup Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 37 ---------------------------------- 1 file changed, 37 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index f8bacd34aea..669ddce51c1 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -346,27 +346,6 @@ namespace lexicographic { namespace { -/** - * @brief has_mixed_lists_and_structs - * @param input - * @return - */ -// bool has_mixed_lists_and_structs(column_view const& input) -//{ -// auto const id = input.type().id(); - -// if (id == type_id::LIST) { -// auto const child = input.child(lists_column_view::child_column_index); -// return child.type().id() == type_id::STRUCT || has_mixed_lists_and_structs(child); -// } else if (id == type_id::STRUCT) { -// return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { -// return child.type().id() == type_id::LIST || has_mixed_lists_and_structs(child); -// }); -// } - -// return false; -//} - /** * @brief has_structs_of_lists * @param input @@ -385,22 +364,6 @@ bool has_structs_of_lists(column_view const& input) return false; } -/** - * @brief has_structs_of_lists - * @param input - * @return - */ -// bool has_lists_of_structs(column_view const& input) -//{ -// if (input.type().id() == type_id::LIST) { -// return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { -// return child.type().id() == type_id::STRUCT || has_lists_of_structs(child); -// }); -// } - -// return false; -//} - /** * @brief flatten_lists * @param input From 6d45f8f847b952ed425f0a4851d96c09a10cc391 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 19:08:03 -0800 Subject: [PATCH 015/140] Fix tests --- cpp/tests/structs/utilities_tests.cpp | 52 +++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/cpp/tests/structs/utilities_tests.cpp b/cpp/tests/structs/utilities_tests.cpp index f27290c3e06..83a9bcc9517 100644 --- a/cpp/tests/structs/utilities_tests.cpp +++ b/cpp/tests/structs/utilities_tests.cpp @@ -52,11 +52,11 @@ TYPED_TEST(TypedStructUtilitiesTest, ListsAtTopLevel) auto lists_col = lists{{0, 1}, {22, 33}, {44, 55, 66}}; auto nums_col = nums{{0, 1, 2}, cudf::test::iterators::null_at(6)}; - auto table = cudf::table_view{{lists_col, nums_col}}; + auto table = cudf::table_view{{lists_col, nums_col}}; + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); - CUDF_TEST_EXPECT_TABLES_EQUAL(table, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + CUDF_TEST_EXPECT_TABLES_EQUAL(table, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, NestedListsUnsupported) @@ -88,11 +88,11 @@ TYPED_TEST(TypedStructUtilitiesTest, NoStructs) {"", "1", "22", "333", "4444", "55555", "666666"}, cudf::test::iterators::null_at(1)}; auto nuther_nums_col = nums{{0, 1, 2, 3, 4, 5, 6}, cudf::test::iterators::null_at(6)}; - auto table = cudf::table_view{{nums_col, strings_col, nuther_nums_col}}; + auto table = cudf::table_view{{nums_col, strings_col, nuther_nums_col}}; + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); - CUDF_TEST_EXPECT_TABLES_EQUAL(table, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + CUDF_TEST_EXPECT_TABLES_EQUAL(table, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, SingleLevelStruct) @@ -116,9 +116,9 @@ TYPED_TEST(TypedStructUtilitiesTest, SingleLevelStruct) auto expected = cudf::table_view{ {expected_nums_col_1, expected_structs_col, expected_nums_col_2, expected_strings_col}}; - CUDF_TEST_EXPECT_TABLES_EQUAL(expected, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); + CUDF_TEST_EXPECT_TABLES_EQUAL(expected, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, SingleLevelStructWithNulls) @@ -144,9 +144,9 @@ TYPED_TEST(TypedStructUtilitiesTest, SingleLevelStructWithNulls) auto expected = cudf::table_view{ {expected_nums_col_1, expected_structs_col, expected_nums_col_2, expected_strings_col}}; - CUDF_TEST_EXPECT_TABLES_EQUAL(expected, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); + CUDF_TEST_EXPECT_TABLES_EQUAL(expected, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, StructOfStruct) @@ -183,9 +183,9 @@ TYPED_TEST(TypedStructUtilitiesTest, StructOfStruct) expected_nums_col_3, expected_strings_col}}; - CUDF_TEST_EXPECT_TABLES_EQUAL(expected, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); + CUDF_TEST_EXPECT_TABLES_EQUAL(expected, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, StructOfStructWithNullsAtLeafLevel) @@ -223,9 +223,9 @@ TYPED_TEST(TypedStructUtilitiesTest, StructOfStructWithNullsAtLeafLevel) expected_nums_col_3, expected_strings_col}}; - CUDF_TEST_EXPECT_TABLES_EQUAL(expected, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); + CUDF_TEST_EXPECT_TABLES_EQUAL(expected, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, StructOfStructWithNullsAtTopLevel) @@ -264,9 +264,9 @@ TYPED_TEST(TypedStructUtilitiesTest, StructOfStructWithNullsAtTopLevel) expected_nums_col_3, expected_strings_col}}; - CUDF_TEST_EXPECT_TABLES_EQUAL(expected, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); + CUDF_TEST_EXPECT_TABLES_EQUAL(expected, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, StructOfStructWithNullsAtAllLevels) @@ -305,9 +305,9 @@ TYPED_TEST(TypedStructUtilitiesTest, StructOfStructWithNullsAtAllLevels) expected_nums_col_3, expected_strings_col}}; - CUDF_TEST_EXPECT_TABLES_EQUAL(expected, - cudf::structs::detail::flatten_nested_columns( - table, {}, {}, cudf::structs::detail::column_nullability::FORCE)); + auto flattened_table = cudf::structs::detail::flatten_nested_columns( + table, {}, {}, cudf::structs::detail::column_nullability::FORCE); + CUDF_TEST_EXPECT_TABLES_EQUAL(expected, flattened_table->flattened_columns()); } TYPED_TEST(TypedStructUtilitiesTest, ListsAreUnsupported) From a242dffdd9c4308d1ee0182af0410859a2c8046b Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 21:45:31 -0800 Subject: [PATCH 016/140] Fix orders Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 669ddce51c1..d71b0c84588 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -412,18 +412,26 @@ table_view flatten_lists(table_view const& input) * @param t */ std::pair> -flatten_nested_lists_or_structs(table_view const& input) +flatten_nested_lists_or_structs(table_view const& input, + host_span column_order, + host_span null_precedence) { - // Firstly, extract lists of structs (if any) into multiple lists of primitive types. - auto lists_flattened = flatten_lists(input); + if (std::any_of(input.begin(), input.end(), has_structs_of_lists)) { + auto structs_flattened = cudf::structs::detail::flatten_nested_columns( + input, + std::vector{column_order.begin(), column_order.end()}, + std::vector{null_precedence.begin(), null_precedence.end()}, + cudf::structs::detail::column_nullability::FORCE); + + // TODO: pass in column order/null preced + // Extract lists of structs (if any) into multiple lists of primitive types. + auto output_table = flatten_lists(structs_flattened->flattened_columns()); - if (std::any_of(lists_flattened.begin(), lists_flattened.end(), has_structs_of_lists)) { - auto structs_flattened = cudf::structs::detail::flatten_nested_columns(lists_flattened, {}, {}); - auto output_table = flatten_lists(structs_flattened->flattened_columns()); return {std::move(output_table), std::move(structs_flattened)}; } - return {std::move(lists_flattened), nullptr}; + // TODO: pass in column order/null preced + return {flatten_lists(input), nullptr}; } } // namespace @@ -434,11 +442,15 @@ std::shared_ptr preprocessed_table::create( host_span null_precedence, rmm::cuda_stream_view stream) { - auto [flattened_t, flattened_t_aux_data] = flatten_nested_lists_or_structs(t); + auto [flattened_t, flattened_t_aux_data] = + flatten_nested_lists_or_structs(t, column_order, null_precedence); check_lex_compatibility(flattened_t); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = - decompose_structs(flattened_t, column_order, null_precedence); + flattened_t_aux_data + ? decompose_structs( + flattened_t, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) + : decompose_structs(flattened_t, {}, {}); auto d_t = table_device_view::create(verticalized_lhs, stream); auto d_column_order = detail::make_device_uvector_async(new_column_order, stream); From 8e772167fe369bd75cfa18ce3c1cf47833b6e343 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 3 Mar 2023 21:48:20 -0800 Subject: [PATCH 017/140] Fix orders again Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index d71b0c84588..9275b6c6226 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -450,7 +450,7 @@ std::shared_ptr preprocessed_table::create( flattened_t_aux_data ? decompose_structs( flattened_t, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) - : decompose_structs(flattened_t, {}, {}); + : decompose_structs(flattened_t, column_order, null_precedence); auto d_t = table_device_view::create(verticalized_lhs, stream); auto d_column_order = detail::make_device_uvector_async(new_column_order, stream); From 12388531e34ba4f700f2e93046c17bbce75b6ad4 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 4 Mar 2023 20:48:00 -0800 Subject: [PATCH 018/140] Cleanup Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 64 +++++----------------------------- 1 file changed, 8 insertions(+), 56 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 9275b6c6226..f02043c8820 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -351,87 +351,39 @@ namespace { * @param input * @return */ -bool has_structs_of_lists(column_view const& input) +bool has_nested_structs_of_lists(column_view const& input) { if (input.type().id() == type_id::STRUCT) { return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { - return child.type().id() == type_id::LIST || has_structs_of_lists(child); + return child.type().id() == type_id::LIST || has_nested_structs_of_lists(child); }); } else if (input.type().id() == type_id::LIST) { - return has_structs_of_lists(input.child(lists_column_view::child_column_index)); + return has_nested_structs_of_lists(input.child(lists_column_view::child_column_index)); } return false; } -/** - * @brief flatten_lists - * @param input - * @return - */ -std::vector flatten_lists_column(column_view const& input) -{ - if (input.type().id() != type_id::LIST || - input.child(lists_column_view::child_column_index).type().id() != type_id::STRUCT) { - return {input}; - } - - auto const offsets = input.child(lists_column_view::offsets_column_index); - auto const children = input.child(lists_column_view::child_column_index); - std::vector output; - - for (auto it = children.child_begin(); it != children.child_end(); ++it) { - auto const new_column = column_view{data_type{type_id::LIST}, - input.size(), - nullptr, - input.null_mask(), - input.null_count(), - input.offset(), - {offsets, *it}}; - // The new column may still be lists of structs, thus we recursively call this: - auto const flattened_new_column = flatten_lists_column(new_column); - output.insert(output.end(), flattened_new_column.begin(), flattened_new_column.end()); - } - - return output; -} - -table_view flatten_lists(table_view const& input) -{ - std::vector out_cols; - for (auto it = input.begin(); it != input.end(); ++it) { - auto const flattened = flatten_lists_column(*it); - out_cols.insert(out_cols.end(), flattened.begin(), flattened.end()); - } - - return table_view{out_cols}; -} - /** * @brief flatten * @param t */ std::pair> -flatten_nested_lists_or_structs(table_view const& input, +flatten_nested_structs_of_lists(table_view const& input, host_span column_order, host_span null_precedence) { - if (std::any_of(input.begin(), input.end(), has_structs_of_lists)) { + if (std::any_of(input.begin(), input.end(), has_nested_structs_of_lists)) { auto structs_flattened = cudf::structs::detail::flatten_nested_columns( input, std::vector{column_order.begin(), column_order.end()}, std::vector{null_precedence.begin(), null_precedence.end()}, cudf::structs::detail::column_nullability::FORCE); - - // TODO: pass in column order/null preced - // Extract lists of structs (if any) into multiple lists of primitive types. - auto output_table = flatten_lists(structs_flattened->flattened_columns()); - + auto output_table = structs_flattened->flattened_columns(); return {std::move(output_table), std::move(structs_flattened)}; } - // TODO: pass in column order/null preced - return {flatten_lists(input), nullptr}; + return {input, nullptr}; } } // namespace @@ -443,7 +395,7 @@ std::shared_ptr preprocessed_table::create( rmm::cuda_stream_view stream) { auto [flattened_t, flattened_t_aux_data] = - flatten_nested_lists_or_structs(t, column_order, null_precedence); + flatten_nested_structs_of_lists(t, column_order, null_precedence); check_lex_compatibility(flattened_t); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = From ec45e324dabb9a80bfc48691f70c06120177afbf Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 4 Mar 2023 20:49:39 -0800 Subject: [PATCH 019/140] Update tests Signed-off-by: Nghia Truong --- cpp/tests/sort/nested_types_tests.cpp | 94 +++------------------------ 1 file changed, 8 insertions(+), 86 deletions(-) diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp index 371fc182107..2f1dd61b087 100644 --- a/cpp/tests/sort/nested_types_tests.cpp +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -26,115 +26,37 @@ using int32s_lists = cudf::test::lists_column_wrapper; using int32s_col = cudf::test::fixed_width_column_wrapper; using structs_col = cudf::test::structs_column_wrapper; -namespace { -std::vector flatten_lists_of_structs(cudf::column_view const& input) -{ - if (input.type().id() != cudf::type_id::LIST || - input.child(cudf::lists_column_view::child_column_index).type().id() != - cudf::type_id::STRUCT) { - return {input}; - } - - auto const offsets = input.child(cudf::lists_column_view::offsets_column_index); - auto const children = input.child(cudf::lists_column_view::child_column_index); - std::vector output; - - for (auto it = children.child_begin(); it != children.child_end(); ++it) { - auto const new_column = cudf::column_view{cudf::data_type{cudf::type_id::LIST}, - input.size(), - nullptr, - input.null_mask(), - input.null_count(), - input.offset(), - {offsets, *it}}; - // The new column may still be lists of structs, thus we recursively call this: - auto const flattened_new_column = flatten_lists_of_structs(new_column); - output.insert(output.end(), flattened_new_column.begin(), flattened_new_column.end()); - } - - return output; -} -} // namespace - struct structs_test : public cudf::test::BaseFixture { }; TEST_F(structs_test, StructsHaveLists) { auto const input = [] { - auto child1 = int32s_lists{{1, 1}, {2, 2}, {3, 3}, {1, 1}, {2, 2}}; - auto child2 = int32s_col{1, 2, 3, 1, 2}; - return structs_col{{child1, child2}}; + auto child1 = int32s_lists{ + {-341142443}, {2147483647}, {-100515324}, {-1549307622, 1285924257}, {1, 1727289611}}; + // auto child1 = int32s_lists{{1, 1}, {2, 2}, {3, 3}, {1, 1}, {-10, -11}, {}, {2, 2}}; + // auto child2 = int32s_col{1, 2, 3, 1, 2, 1, 2}; + return structs_col{{child1}}; }(); - printf("line %d\n", __LINE__); - cudf::test::print(input); - - if (1) { + { auto const order = cudf::sorted_order(cudf::table_view{{input}}); printf("line %d\n", __LINE__); cudf::test::print(*order); auto const sorted = cudf::sort(cudf::table_view{{input}}); - printf("line %d\n", __LINE__); cudf::test::print(sorted->get_column(0).view()); } { - auto const flattened = - cudf::structs::detail::flatten_nested_columns(cudf::table_view{{input}}, {}, {}); - auto const order = cudf::sorted_order(flattened->flattened_columns()); + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); printf("line %d\n", __LINE__); cudf::test::print(*order); - auto const sorted = cudf::sort(cudf::table_view{{flattened->flattened_columns()}}); - - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); - } -} - -TEST_F(structs_test, ListsHaveStructs) -{ - auto const input = [] { - auto const get_structs = [] { - auto child1 = int32s_col{1, 2, 3, 0, 1, 2, 4, 5, 0, 1}; - auto child2 = int32s_col{11, 12, 13, 10, 11, 12, 14, 15, 10, 11}; - return structs_col{{child1, child2}}; - }; - - return cudf::make_lists_column( - 4, int32s_col{0, 3, 6, 8, 10}.release(), get_structs().release(), 0, {}); - }(); - - printf("line %d\n", __LINE__); - cudf::test::print(*input); - - if (1) { - auto const order = cudf::sorted_order(cudf::table_view{{*input}}); - - printf("line %d\n", __LINE__); - cudf::test::print(*order); - - auto const sorted = cudf::sort(cudf::table_view{{*input}}); - - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); - } - - { - auto const flattened = flatten_lists_of_structs(*input); - - auto const order = cudf::sorted_order(cudf::table_view{flattened}); - - printf("line %d\n", __LINE__); - cudf::test::print(*order); - - auto const sorted = cudf::sort(cudf::table_view{flattened}); - + auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); printf("line %d\n", __LINE__); cudf::test::print(sorted->get_column(0).view()); } From 71fc858cc2719830fb36468a2cd7071d83b7de19 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 4 Mar 2023 21:29:51 -0800 Subject: [PATCH 020/140] Add variable storing auxiliary data Signed-off-by: Nghia Truong --- .../cudf/table/experimental/row_operators.cuh | 8 +- cpp/src/table/row_operators.cu | 76 ++++++++++++++++--- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 93be727a3e4..061e84f7cb6 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -735,14 +735,16 @@ struct preprocessed_table { rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::unique_ptr&& flattened_input_aux_data); + std::unique_ptr&& flattened_input_aux_data, + std::vector>&& transformed_structs_columns); preprocessed_table( table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::unique_ptr&& flattened_input_aux_data); + std::unique_ptr&& flattened_input_aux_data, + std::vector>&& transformed_structs_columns); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -813,6 +815,8 @@ struct preprocessed_table { // Auxiliary data generated from `cudf::structs::detail::flatten_nested_columns` // that needs to be kept alive. std::unique_ptr _flattened_input_aux_data; + + std::vector> _transformed_structs_aux_data; }; /** diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index f02043c8820..e78cb3b6afe 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -285,13 +285,13 @@ void check_lex_compatibility(table_view const& input) column_checker_fn_t check_column = [&](column_view const& c) { if (c.type().id() == type_id::LIST) { auto const& list_col = lists_column_view(c); - // CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, - // "Cannot lexicographic compare a table with a LIST of STRUCT column"); + CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, + "Cannot lexicographic compare a table with a LIST of STRUCT column"); check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { - // CUDF_EXPECTS(child->type().id() != type_id::LIST, - // "Cannot lexicographic compare a table with a STRUCT of LIST column"); + CUDF_EXPECTS(child->type().id() != type_id::LIST, + "Cannot lexicographic compare a table with a STRUCT of LIST column"); check_column(*child); } } @@ -346,6 +346,51 @@ namespace lexicographic { namespace { +/** + * @brief has_structs_of_lists + * @param input + * @return + */ +bool has_nested_lists_of_structs(column_view const& input) +{ + if (input.type().id() == type_id::LIST) { + auto const child = input.child(lists_column_view::child_column_index); + return child.type().id() == type_id::STRUCT || has_nested_lists_of_structs(child); + } else if (input.type().id() == type_id::STRUCT) { + return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { + return has_nested_lists_of_structs(child); + }); + } + + return false; +} + +std::pair>> transform_lists_of_structs( + column_view const& input) +{ + return {column_view{}, std::vector>{}}; +} + +std::pair>> transform_lists_of_structs( + table_view const& input) +{ + std::vector transformed_columns; + std::vector> aux_data; + std::for_each(input.begin(), input.end(), [&](auto const& col) { + if (!has_nested_lists_of_structs(col)) { + transformed_columns.push_back(col); + } else { + auto [transformed_col, col_aux_data] = transform_lists_of_structs(col); + transformed_columns.push_back(transformed_col); + aux_data.insert(aux_data.end(), + std::make_move_iterator(col_aux_data.begin()), + std::make_move_iterator(col_aux_data.end())); + } + }); + + return {table_view{transformed_columns}, std::move(aux_data)}; +} + /** * @brief has_structs_of_lists * @param input @@ -394,8 +439,11 @@ std::shared_ptr preprocessed_table::create( host_span null_precedence, rmm::cuda_stream_view stream) { + auto [transformed_t, transformed_aux_data] = transform_lists_of_structs(t); + auto [flattened_t, flattened_t_aux_data] = - flatten_nested_structs_of_lists(t, column_order, null_precedence); + flatten_nested_structs_of_lists(transformed_t, column_order, null_precedence); + check_lex_compatibility(flattened_t); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = @@ -418,14 +466,16 @@ std::shared_ptr preprocessed_table::create( std::move(d_depths), std::move(dremel_data), std::move(d_dremel_device_view), - std::move(flattened_t_aux_data))); + std::move(flattened_t_aux_data), + std::move(transformed_aux_data))); } else { return std::shared_ptr( new preprocessed_table(std::move(d_t), std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), - std::move(flattened_t_aux_data))); + std::move(flattened_t_aux_data), + std::move(transformed_aux_data))); } } @@ -436,14 +486,16 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::unique_ptr&& flattened_input_aux_data) + std::unique_ptr&& flattened_input_aux_data, + std::vector>&& transformed_structs_columns) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), - _flattened_input_aux_data(std::move(flattened_input_aux_data)) + _flattened_input_aux_data(std::move(flattened_input_aux_data)), + _transformed_structs_aux_data(std::move(transformed_structs_columns)) { } @@ -452,14 +504,16 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::unique_ptr&& flattened_input_aux_data) + std::unique_ptr&& flattened_input_aux_data, + std::vector>&& transformed_structs_columns) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data{}, _dremel_device_views{}, - _flattened_input_aux_data(std::move(flattened_input_aux_data)) + _flattened_input_aux_data(std::move(flattened_input_aux_data)), + _transformed_structs_aux_data(std::move(transformed_structs_columns)) { } From c8b063474cabc571acdfcfdad863f362c4534d49 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sun, 5 Mar 2023 07:04:38 -0800 Subject: [PATCH 021/140] Support lists of structs Signed-off-by: Nghia Truong --- cpp/include/cudf/detail/sorting.hpp | 15 ++++++++ cpp/src/table/row_operators.cu | 49 +++++++++++++++++++++------ cpp/tests/sort/nested_types_tests.cpp | 29 ++++++++++++++++ 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/cpp/include/cudf/detail/sorting.hpp b/cpp/include/cudf/detail/sorting.hpp index 9c936e6c659..5e9efbd1e30 100644 --- a/cpp/include/cudf/detail/sorting.hpp +++ b/cpp/include/cudf/detail/sorting.hpp @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -61,6 +62,20 @@ std::unique_ptr sort_by_key(table_view const& values, rmm::cuda_stream_view stream, rmm::mr::device_memory_resource* mr); +/** + * @copydoc cudf::rank + * + * @param[in] stream CUDA stream used for device memory operations and kernel launches. + */ +std::unique_ptr rank(column_view const& input, + rank_method method, + order column_order, + null_policy null_handling, + null_order null_precedence, + bool percentage, + rmm::cuda_stream_view stream, + rmm::mr::device_memory_resource* mr); + /** * @copydoc cudf::stable_sort_by_key * diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index e78cb3b6afe..8c079890b86 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -368,7 +369,39 @@ bool has_nested_lists_of_structs(column_view const& input) std::pair>> transform_lists_of_structs( column_view const& input) { - return {column_view{}, std::vector>{}}; + if (!has_nested_lists_of_structs(input)) { + return {input, std::vector>{}}; + } + + std::vector> aux_data; + if (input.type().id() == type_id::LIST) { + auto const child = input.child(lists_column_view::child_column_index); + // TODO: This doesn't work with list of struct of list. + // This also doesn't work with nested list of struct of struct. + if (child.type().id() == type_id::STRUCT) { + aux_data.emplace_back(cudf::detail::rank(child, + rank_method::DENSE, + order::ASCENDING, + null_policy::EXCLUDE, + null_order::AFTER, + false, + cudf::get_default_stream(), + rmm::mr::get_current_device_resource())); + auto new_column = column_view{ + data_type{type_id::LIST}, + input.size(), + nullptr, + input.null_mask(), + input.null_count(), + input.offset(), + {input.child(lists_column_view::offsets_column_index), aux_data.back()->view()}}; + + return {std::move(new_column), std::move(aux_data)}; + } + } + + // TODO + return {input, std::vector>{}}; } std::pair>> transform_lists_of_structs( @@ -377,15 +410,11 @@ std::pair>> transform_lists_of_s std::vector transformed_columns; std::vector> aux_data; std::for_each(input.begin(), input.end(), [&](auto const& col) { - if (!has_nested_lists_of_structs(col)) { - transformed_columns.push_back(col); - } else { - auto [transformed_col, col_aux_data] = transform_lists_of_structs(col); - transformed_columns.push_back(transformed_col); - aux_data.insert(aux_data.end(), - std::make_move_iterator(col_aux_data.begin()), - std::make_move_iterator(col_aux_data.end())); - } + auto [transformed_col, col_aux_data] = transform_lists_of_structs(col); + transformed_columns.push_back(transformed_col); + aux_data.insert(aux_data.end(), + std::make_move_iterator(col_aux_data.begin()), + std::make_move_iterator(col_aux_data.end())); }); return {table_view{transformed_columns}, std::move(aux_data)}; diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp index 2f1dd61b087..7c5d7b42630 100644 --- a/cpp/tests/sort/nested_types_tests.cpp +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -61,3 +61,32 @@ TEST_F(structs_test, StructsHaveLists) cudf::test::print(sorted->get_column(0).view()); } } + +TEST_F(structs_test, ListsHaveStructs) +{ + auto const input = [] { + auto const get_structs = [] { + auto child1 = int32s_col{1, 2, 3, 0, 1, 2, 4, 5, 0, 1}; + // auto child2 = int32s_col{11, 12, 13, 10, 11, 12, 14, 15, 10, 11}; + return structs_col{{child1 /*, child2*/}}; + }; + + return cudf::make_lists_column( + 4, int32s_col{0, 3, 6, 8, 10}.release(), get_structs().release(), 0, {}); + }(); + + printf("line %d\n", __LINE__); + cudf::test::print(*input); + + if (1) { + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + + printf("line %d\n", __LINE__); + cudf::test::print(*order); + + auto const sorted = cudf::sort(cudf::table_view{{*input}}); + + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); + } +} From 4d35317334722d706fe16b5cdaa2debd2ac9bc8b Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sun, 5 Mar 2023 08:52:03 -0800 Subject: [PATCH 022/140] Fix copyright year --- cpp/tests/structs/utilities_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/structs/utilities_tests.cpp b/cpp/tests/structs/utilities_tests.cpp index 83a9bcc9517..62b9ae6da7a 100644 --- a/cpp/tests/structs/utilities_tests.cpp +++ b/cpp/tests/structs/utilities_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, NVIDIA CORPORATION. + * Copyright (c) 2021-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From c61beef446e9fd0c8a26ebeca7220dcf9111ea94 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sun, 5 Mar 2023 09:15:12 -0800 Subject: [PATCH 023/140] Fix null order --- cpp/src/table/row_operators.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 8c079890b86..be0208aa1ca 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -383,7 +383,7 @@ std::pair>> transform_lists_of_ rank_method::DENSE, order::ASCENDING, null_policy::EXCLUDE, - null_order::AFTER, + null_order::BEFORE, false, cudf::get_default_stream(), rmm::mr::get_current_device_resource())); From 37913be4def2894f07f1a65fcffd1469d1b3c320 Mon Sep 17 00:00:00 2001 From: Karthikeyan Natarajan Date: Mon, 6 Mar 2023 18:23:12 +0530 Subject: [PATCH 024/140] include cleanup for cudf/detail/structs/utilities.hpp --- cpp/src/groupby/groupby.cu | 3 +-- cpp/src/groupby/sort/group_rank_scan.cu | 1 - cpp/src/groupby/sort/sort_helper.cu | 1 - cpp/src/reductions/simple.cuh | 1 - cpp/src/sort/is_sorted.cu | 1 - cpp/src/sort/sort_impl.cuh | 3 +-- 6 files changed, 2 insertions(+), 8 deletions(-) diff --git a/cpp/src/groupby/groupby.cu b/cpp/src/groupby/groupby.cu index 0e90848af3a..1979108eaa2 100644 --- a/cpp/src/groupby/groupby.cu +++ b/cpp/src/groupby/groupby.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/cpp/src/groupby/sort/group_rank_scan.cu b/cpp/src/groupby/sort/group_rank_scan.cu index 479ce166724..9ebac957e8f 100644 --- a/cpp/src/groupby/sort/group_rank_scan.cu +++ b/cpp/src/groupby/sort/group_rank_scan.cu @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/cpp/src/groupby/sort/sort_helper.cu b/cpp/src/groupby/sort/sort_helper.cu index b53955472b1..4dd1375b5a5 100644 --- a/cpp/src/groupby/sort/sort_helper.cu +++ b/cpp/src/groupby/sort/sort_helper.cu @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/cpp/src/reductions/simple.cuh b/cpp/src/reductions/simple.cuh index 5fe7b91e28a..c7c0d400106 100644 --- a/cpp/src/reductions/simple.cuh +++ b/cpp/src/reductions/simple.cuh @@ -20,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/cpp/src/sort/is_sorted.cu b/cpp/src/sort/is_sorted.cu index 356c58b1c22..4c5ad1ef0ea 100644 --- a/cpp/src/sort/is_sorted.cu +++ b/cpp/src/sort/is_sorted.cu @@ -15,7 +15,6 @@ */ #include -#include #include #include #include diff --git a/cpp/src/sort/sort_impl.cuh b/cpp/src/sort/sort_impl.cuh index fc024b42616..421a998c86f 100644 --- a/cpp/src/sort/sort_impl.cuh +++ b/cpp/src/sort/sort_impl.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. + * Copyright (c) 2020-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ #include #include -#include #include #include #include From 5ce6f6f16ac0782865a6c4f836d5700d994607b6 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 09:19:44 -0800 Subject: [PATCH 025/140] Cleanup --- cpp/src/groupby/sort/sort_helper.cu | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/src/groupby/sort/sort_helper.cu b/cpp/src/groupby/sort/sort_helper.cu index 4dd1375b5a5..ebafcd75e6d 100644 --- a/cpp/src/groupby/sort/sort_helper.cu +++ b/cpp/src/groupby/sort/sort_helper.cu @@ -60,8 +60,6 @@ sort_groupby_helper::sort_groupby_helper(table_view const& keys, _include_null_keys(include_null_keys), _null_precedence(null_precedence) { - using namespace cudf::structs::detail; - // Cannot depend on caller's sorting if the column contains nulls, // and null values are to be excluded. // Re-sort the data, to filter out nulls more easily. From fdd9b23bb8577316237b02fc8e86c8835c2d39bd Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 10:20:54 -0800 Subject: [PATCH 026/140] Fix comments --- cpp/include/cudf/table/experimental/row_operators.cuh | 2 ++ cpp/src/table/row_operators.cu | 3 +++ 2 files changed, 5 insertions(+) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 061e84f7cb6..96123021260 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -816,6 +816,8 @@ struct preprocessed_table { // that needs to be kept alive. std::unique_ptr _flattened_input_aux_data; + // Auxiliary data generated from transforming lists-of-structs into lists-of-integer + // that needs to be kept alive. std::vector> _transformed_structs_aux_data; }; diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index be0208aa1ca..ef7574892e4 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -468,8 +468,11 @@ std::shared_ptr preprocessed_table::create( host_span null_precedence, rmm::cuda_stream_view stream) { + // Firstly, transform any (nested) lists-of-structs column into lists-of-ranks where ranks are + // integer numbers computed from the struct child of the input lists column. auto [transformed_t, transformed_aux_data] = transform_lists_of_structs(t); + // Next, flatten any structs-of-lists column. auto [flattened_t, flattened_t_aux_data] = flatten_nested_structs_of_lists(transformed_t, column_order, null_precedence); From 35b87bade7b417b0b3f12feb4ddfb30ab3b2ffe1 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 13:13:55 -0800 Subject: [PATCH 027/140] Support arbitrary nested input --- cpp/src/table/row_operators.cu | 117 +++++++++++++++++---------------- 1 file changed, 60 insertions(+), 57 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index ef7574892e4..32373978445 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -352,69 +352,73 @@ namespace { * @param input * @return */ -bool has_nested_lists_of_structs(column_view const& input) +// bool has_nested_lists_of_structs(column_view const& input) +//{ +// if (input.type().id() == type_id::LIST) { +// auto const child = input.child(lists_column_view::child_column_index); +// return child.type().id() == type_id::STRUCT || has_nested_lists_of_structs(child); +// } else if (input.type().id() == type_id::STRUCT) { +// return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { +// return has_nested_lists_of_structs(child); +// }); +// } + +// return false; +//} + +std::pair> transform_lists_of_structs( + column_view const& input, rmm::cuda_stream_view stream) { - if (input.type().id() == type_id::LIST) { - auto const child = input.child(lists_column_view::child_column_index); - return child.type().id() == type_id::STRUCT || has_nested_lists_of_structs(child); - } else if (input.type().id() == type_id::STRUCT) { - return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { - return has_nested_lists_of_structs(child); - }); - } - - return false; -} - -std::pair>> transform_lists_of_structs( - column_view const& input) -{ - if (!has_nested_lists_of_structs(input)) { - return {input, std::vector>{}}; - } + // if (!has_nested_lists_of_structs(input)) { return {input, nullptr}; } + + auto const make_transformed_input = [&](auto const& new_child) { + return column_view{data_type{type_id::LIST}, + input.size(), + nullptr, + input.null_mask(), + input.null_count(), + input.offset(), + {input.child(lists_column_view::offsets_column_index), new_child}}; + }; - std::vector> aux_data; if (input.type().id() == type_id::LIST) { + // Should not use sliced child because we reuse the input's offset value and offsets child. auto const child = input.child(lists_column_view::child_column_index); - // TODO: This doesn't work with list of struct of list. - // This also doesn't work with nested list of struct of struct. + if (child.type().id() == type_id::STRUCT) { - aux_data.emplace_back(cudf::detail::rank(child, - rank_method::DENSE, - order::ASCENDING, - null_policy::EXCLUDE, - null_order::BEFORE, - false, - cudf::get_default_stream(), - rmm::mr::get_current_device_resource())); - auto new_column = column_view{ - data_type{type_id::LIST}, - input.size(), - nullptr, - input.null_mask(), - input.null_count(), - input.offset(), - {input.child(lists_column_view::offsets_column_index), aux_data.back()->view()}}; - - return {std::move(new_column), std::move(aux_data)}; + auto ranks = cudf::detail::rank(child, + rank_method::DENSE, + order::ASCENDING, + null_policy::EXCLUDE, + null_order::BEFORE, + false, + stream, + rmm::mr::get_current_device_resource()); + auto transformed_input = make_transformed_input(ranks->view()); + return {std::move(transformed_input), std::move(ranks)}; + } else if (child.type().id() == type_id::LIST) { + auto [new_child, ranks_cols] = transform_lists_of_structs(child, stream); + if (ranks_cols) { + auto transformed_input = make_transformed_input(new_child); + return {transformed_input, std::move(ranks_cols)}; + } } + } else if (input.type().id() == type_id::STRUCT) { + CUDF_UNREACHABLE("Structs columns should be flattened before calling this function."); } - // TODO - return {input, std::vector>{}}; + return {input, nullptr}; } std::pair>> transform_lists_of_structs( - table_view const& input) + table_view const& input, rmm::cuda_stream_view stream) { std::vector transformed_columns; std::vector> aux_data; std::for_each(input.begin(), input.end(), [&](auto const& col) { - auto [transformed_col, col_aux_data] = transform_lists_of_structs(col); + auto [transformed_col, col_aux_data] = transform_lists_of_structs(col, stream); transformed_columns.push_back(transformed_col); - aux_data.insert(aux_data.end(), - std::make_move_iterator(col_aux_data.begin()), - std::make_move_iterator(col_aux_data.end())); + if (col_aux_data) { aux_data.emplace_back(std::move(col_aux_data)); } }); return {table_view{transformed_columns}, std::move(aux_data)}; @@ -468,28 +472,27 @@ std::shared_ptr preprocessed_table::create( host_span null_precedence, rmm::cuda_stream_view stream) { - // Firstly, transform any (nested) lists-of-structs column into lists-of-ranks where ranks are - // integer numbers computed from the struct child of the input lists column. - auto [transformed_t, transformed_aux_data] = transform_lists_of_structs(t); - - // Next, flatten any structs-of-lists column. + // Firstly, flatten any structs-of-lists column. auto [flattened_t, flattened_t_aux_data] = - flatten_nested_structs_of_lists(transformed_t, column_order, null_precedence); + flatten_nested_structs_of_lists(t, column_order, null_precedence); + + // Next, transform any (nested) lists-of-structs column into lists-of-integers column. + auto [transformed_t, transformed_aux_data] = transform_lists_of_structs(flattened_t, stream); - check_lex_compatibility(flattened_t); + check_lex_compatibility(transformed_t); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = flattened_t_aux_data ? decompose_structs( - flattened_t, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) - : decompose_structs(flattened_t, column_order, null_precedence); + transformed_t, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) + : decompose_structs(transformed_t, column_order, null_precedence); auto d_t = table_device_view::create(verticalized_lhs, stream); auto d_column_order = detail::make_device_uvector_async(new_column_order, stream); auto d_null_precedence = detail::make_device_uvector_async(new_null_precedence, stream); auto d_depths = detail::make_device_uvector_async(verticalized_col_depths, stream); - if (detail::has_nested_columns(flattened_t)) { + if (detail::has_nested_columns(transformed_t)) { auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(verticalized_lhs, stream); return std::shared_ptr( new preprocessed_table(std::move(d_t), From 543833644556ad5292d921902455f99a4349c7b4 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 14:23:47 -0800 Subject: [PATCH 028/140] Cleanup and add docs --- cpp/src/table/row_operators.cu | 52 ++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 32373978445..a10d75f1177 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -348,29 +348,22 @@ namespace lexicographic { namespace { /** - * @brief has_structs_of_lists - * @param input - * @return + * @brief Transform a lists-of-structs column into lists-of-integers column. + * + * For lists-of-structs column at any nested level, the child structs column will be replaced by an + * integer column of its ranks generated using `cudf::rank()`. + * + * If the input column is not lists-of-structs, or does not contain lists-of-structs at any nested + * level, the input will be passed through. + * + * @param input The input column to transform + * @param stream CUDA stream used for device memory operations and kernel launches + * @return A pair of new column_view representing the transformed input and the generated rank + * (integer) column which needs to be kept alive. */ -// bool has_nested_lists_of_structs(column_view const& input) -//{ -// if (input.type().id() == type_id::LIST) { -// auto const child = input.child(lists_column_view::child_column_index); -// return child.type().id() == type_id::STRUCT || has_nested_lists_of_structs(child); -// } else if (input.type().id() == type_id::STRUCT) { -// return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { -// return has_nested_lists_of_structs(child); -// }); -// } - -// return false; -//} - std::pair> transform_lists_of_structs( column_view const& input, rmm::cuda_stream_view stream) { - // if (!has_nested_lists_of_structs(input)) { return {input, nullptr}; } - auto const make_transformed_input = [&](auto const& new_child) { return column_view{data_type{type_id::LIST}, input.size(), @@ -410,6 +403,14 @@ std::pair> transform_lists_of_structs( return {input, nullptr}; } +/** + * @brief Transform any lists-of-structs column in a given table into lists-of-integers column. + * + * @param input The input table to transform + * @param stream CUDA stream used for device memory operations and kernel launches + * @return A pair of new table_view representing the transformed input and the generated rank + * (integer) column which needs to be kept alive. + */ std::pair>> transform_lists_of_structs( table_view const& input, rmm::cuda_stream_view stream) { @@ -425,9 +426,9 @@ std::pair>> transform_lists_of_s } /** - * @brief has_structs_of_lists - * @param input - * @return + * @brief Check if the input column has structs-of-lists column at any nested level. + * @param input The input column to check + * @return Boolean value indicating if there is structs-of-lists column */ bool has_nested_structs_of_lists(column_view const& input) { @@ -443,8 +444,11 @@ bool has_nested_structs_of_lists(column_view const& input) } /** - * @brief flatten - * @param t + * @brief Flatten any structs column in the given table. + * + * @param input The input table + * @return A pair of table_view representing the flattened input and an auxiliary data structure + * that needs to be kept alive. */ std::pair> flatten_nested_structs_of_lists(table_view const& input, From cafd1ffe533f0a78f851e856ef80006471377180 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 20:33:51 -0800 Subject: [PATCH 029/140] Fix doxygen --- cpp/include/cudf/detail/structs/utilities.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/include/cudf/detail/structs/utilities.hpp b/cpp/include/cudf/detail/structs/utilities.hpp index 90ae39fb2ce..84c12cea13c 100644 --- a/cpp/include/cudf/detail/structs/utilities.hpp +++ b/cpp/include/cudf/detail/structs/utilities.hpp @@ -162,10 +162,10 @@ class flattened_table { * @param input input table to be flattened * @param column_order column order for input table * @param null_precedence null order for input table - * @param nullability force output to have nullability columns even if input columns - * are all valid - * @return `flatten_result` with flattened table, flattened column order, flattened null precedence, - * alongside the supporting columns and device_buffers for the flattened table. + * @param nullability force output to have nullability columns even if input columns are all valid + * @return A pointer of type `flattened_table` containing flattened columns, flattened column + * orders, flattened null precedence, alongside the supporting columns and device_buffers + * for the flattened table. */ [[nodiscard]] std::unique_ptr flatten_nested_columns( table_view const& input, From 01827e4627f5ef47f1a5bd04f3cda33903252068 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 20:40:58 -0800 Subject: [PATCH 030/140] Fix comments --- cpp/src/table/row_operators.cu | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index a10d75f1177..e9bc7cad21e 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -444,7 +444,10 @@ bool has_nested_structs_of_lists(column_view const& input) } /** - * @brief Flatten any structs column in the given table. + * @brief Flatten the given table if it contains any structs-of-lists column. + * + * If the input table contains any structs-of-lists column, the entire table will be flattened to + * a table of non-struct columns. Otherwise, the input table is passed through. * * @param input The input table * @return A pair of table_view representing the flattened input and an auxiliary data structure @@ -476,7 +479,7 @@ std::shared_ptr preprocessed_table::create( host_span null_precedence, rmm::cuda_stream_view stream) { - // Firstly, flatten any structs-of-lists column. + // Firstly, flatten the input table if it contains any structs-of-lists column. auto [flattened_t, flattened_t_aux_data] = flatten_nested_structs_of_lists(t, column_order, null_precedence); From 5f8ae83001948b447f7431934df8a83b99a18120 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 20:54:59 -0800 Subject: [PATCH 031/140] Add comment --- cpp/src/table/row_operators.cu | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index e9bc7cad21e..61b77d81051 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -459,6 +459,8 @@ flatten_nested_structs_of_lists(table_view const& input, host_span null_precedence) { if (std::any_of(input.begin(), input.end(), has_nested_structs_of_lists)) { + // TODO: Pass in stream parameter. + // Issue: https://github.com/rapidsai/cudf/issues/12349 auto structs_flattened = cudf::structs::detail::flatten_nested_columns( input, std::vector{column_order.begin(), column_order.end()}, From 02b1619d68de48ad5090ab4d20ab3917f0ed5f8d Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 6 Mar 2023 21:14:42 -0800 Subject: [PATCH 032/140] Cleanup --- cpp/src/structs/utilities.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cpp/src/structs/utilities.cpp b/cpp/src/structs/utilities.cpp index 26abbdce89c..7af39df51c5 100644 --- a/cpp/src/structs/utilities.cpp +++ b/cpp/src/structs/utilities.cpp @@ -105,7 +105,6 @@ struct table_flattener { : column_order(column_order), null_precedence(null_precedence), nullability(nullability) { superimpose_nulls(input); - fail_if_unsupported_types(input); } /** @@ -119,12 +118,6 @@ struct table_flattener { this->nullable_data = std::move(tmp_nullable_data); } - void fail_if_unsupported_types(table_view const& input) const - { - // auto const has_lists = std::any_of(input.begin(), input.end(), is_or_has_nested_lists); - // CUDF_EXPECTS(not has_lists, "Flattening LIST columns is not supported."); - } - // Convert null_mask to BOOL8 columns and flatten the struct children in order. void flatten_struct_column(structs_column_view const& col, order col_order, From 3d91de15699b5480dbb7f456b184af6ac49e2dc1 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 7 Mar 2023 11:55:27 -0800 Subject: [PATCH 033/140] Add structs-of-lists tests --- cpp/tests/sort/nested_types_tests.cpp | 288 ++++++++++++++++++++++++-- 1 file changed, 268 insertions(+), 20 deletions(-) diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp index 7c5d7b42630..3f582506bd9 100644 --- a/cpp/tests/sort/nested_types_tests.cpp +++ b/cpp/tests/sort/nested_types_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, NVIDIA CORPORATION. + * Copyright (c) 2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,50 +19,287 @@ #include #include -#include #include using int32s_lists = cudf::test::lists_column_wrapper; using int32s_col = cudf::test::fixed_width_column_wrapper; using structs_col = cudf::test::structs_column_wrapper; -struct structs_test : public cudf::test::BaseFixture { +using namespace cudf::test::iterators; + +constexpr auto null{0}; + +struct NestedStructTest : public cudf::test::BaseFixture { }; -TEST_F(structs_test, StructsHaveLists) +TEST_F(NestedStructTest, SimpleStructsOfListsNoNulls) { auto const input = [] { - auto child1 = int32s_lists{ - {-341142443}, {2147483647}, {-100515324}, {-1549307622, 1285924257}, {1, 1727289611}}; - // auto child1 = int32s_lists{{1, 1}, {2, 2}, {3, 3}, {1, 1}, {-10, -11}, {}, {2, 2}}; - // auto child2 = int32s_col{1, 2, 3, 1, 2, 1, 2}; - return structs_col{{child1}}; + auto child = int32s_lists{{4, 2, 0}, {2}, {0, 5}, {1, 5}, {4, 1}}; + return structs_col{{child}}; }(); { - auto const order = cudf::sorted_order(cudf::table_view{{input}}); + auto const expected_order = int32s_col{2, 3, 1, 4, 0}; + auto const expected_sort = [] { + auto child = int32s_lists{{0, 5}, {1, 5}, {2}, {4, 1}, {4, 2, 0}}; + return structs_col{{child}}; + }(); - printf("line %d\n", __LINE__); - cudf::test::print(*order); + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + auto const sorted = cudf::sort(cudf::table_view{{input}}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } + + { + auto const expected_order = int32s_col{0, 4, 1, 3, 2}; + auto const expected_sort = [] { + auto child = int32s_lists{{4, 2, 0}, {4, 1}, {2}, {1, 5}, {0, 5}}; + return structs_col{{child}}; + }(); + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } +} +TEST_F(NestedStructTest, SimpleStructsOfListsWithNulls) +{ + auto const input = [] { + auto child = + int32s_lists{{{4, 2, null}, null_at(2)}, {2}, {{null, 5}, null_at(0)}, {0, 5}, {4, 1}}; + return structs_col{{child}}; + }(); + + { + auto const expected_order = int32s_col{2, 3, 1, 4, 0}; + auto const expected_sort = [] { + auto child = + int32s_lists{{{null, 5}, null_at(0)}, {0, 5}, {2}, {4, 1}, {{4, 2, null}, null_at(2)}}; + return structs_col{{child}}; + }(); + + auto const order = cudf::sorted_order(cudf::table_view{{input}}); auto const sorted = cudf::sort(cudf::table_view{{input}}); - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); } { - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + auto const expected_order = int32s_col{0, 4, 1, 3, 2}; + auto const expected_sort = [] { + auto child = + int32s_lists{{{4, 2, null}, null_at(2)}, {4, 1}, {2}, {0, 5}, {{null, 5}, null_at(0)}}; + return structs_col{{child}}; + }(); + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - printf("line %d\n", __LINE__); - cudf::test::print(*order); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } +} +TEST_F(NestedStructTest, StructsHaveListsNoNulls) +{ + auto const input = [] { + auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1}}; + }(); + + { + auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; + auto const expected_sort = [] { + auto child0 = int32s_lists{{}, {}, {}, {4, 0}, {4, 1}, {4, 2, 0}, {5}}; + auto child1 = int32s_col{2, 3, 4, 3, 0, 1, 5}; + return structs_col{{child0, child1}}; + }(); + + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + auto const sorted = cudf::sort(cudf::table_view{{input}}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } + + { + auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; + auto const expected_sort = [] { + auto child0 = int32s_lists{{5}, {4, 2, 0}, {4, 1}, {4, 0}, {}, {}, {}}; + auto child1 = int32s_col{5, 1, 0, 3, 4, 3, 2}; + return structs_col{{child0, child1}}; + }(); + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } +} + +TEST_F(NestedStructTest, StructsHaveListsWithNulls) +{ + auto const input = [] { + auto child0 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1}, null_at(2)}; + }(); + + { + auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; + auto const expected_sort = [] { + auto child0 = + int32s_lists{{} /*NULL*/, {}, {}, {}, {4, 0}, {4, 1}, {{4, 2, null}, null_at(2)}, {5}}; + auto child1 = int32s_col{{null, 2, 3, 4, 3, null, 1, 5}, nulls_at({0, 5})}; + return structs_col{{child0, child1}, null_at(0)}; + }(); + + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + auto const sorted = cudf::sort(cudf::table_view{{input}}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } + + { + auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; + auto const expected_sort = [] { + auto child0 = + int32s_lists{{5}, {{4, 2, null}, null_at(2)}, {4, 1}, {4, 0}, {}, {}, {}, {} /*NULL*/}; + auto child1 = int32s_col{{5, 1, null, 3, 4, 3, 2, null}, nulls_at({2, 7})}; + return structs_col{{child0, child1}, null_at(7)}; + }(); + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } +} + +TEST_F(NestedStructTest, StructsOfStructsHaveListsNoNulls) +{ + auto const input = [] { + auto child0 = [] { + auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1}}; + }(); + auto child1 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child2 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1, child2}}; + }(); + + { + auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; + auto const expected_sort = [] { + auto child0 = [] { + auto child0 = int32s_lists{{}, {}, {}, {4, 0}, {4, 1}, {4, 2, 0}, {5}}; + auto child1 = int32s_col{2, 3, 4, 3, 0, 1, 5}; + return structs_col{{child0, child1}}; + }(); + auto child1 = int32s_lists{{}, {}, {}, {4, 0}, {4, 1}, {4, 2, 0}, {5}}; + auto child2 = int32s_col{2, 3, 4, 3, 0, 1, 5}; + return structs_col{{child0, child1, child2}}; + }(); + + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + auto const sorted = cudf::sort(cudf::table_view{{input}}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } + + { + auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; + auto const expected_sort = [] { + auto child0 = [] { + auto child0 = int32s_lists{{5}, {4, 2, 0}, {4, 1}, {4, 0}, {}, {}, {}}; + auto child1 = int32s_col{5, 1, 0, 3, 4, 3, 2}; + return structs_col{{child0, child1}}; + }(); + auto child1 = int32s_lists{{5}, {4, 2, 0}, {4, 1}, {4, 0}, {}, {}, {}}; + auto child2 = int32s_col{5, 1, 0, 3, 4, 3, 2}; + return structs_col{{child0, child1, child2}}; + }(); + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } +} + +TEST_F(NestedStructTest, StructsOfStructsHaveListsWithNulls) +{ + auto const input = [] { + auto child0 = [] { + auto child0 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1}, null_at(2)}; + }(); + auto child1 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child2 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1, child2}, null_at(2)}; + }(); + + { + auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; + auto const expected_sort = [] { + auto child0 = [] { + auto child0 = + int32s_lists{{} /*NULL*/, {}, {}, {}, {4, 0}, {4, 1}, {{4, 2, null}, null_at(2)}, {5}}; + auto child1 = int32s_col{{null, 2, 3, 4, 3, null, 1, 5}, nulls_at({0, 5})}; + return structs_col{{child0, child1}, null_at(0)}; + }(); + auto child1 = + int32s_lists{{} /*NULL*/, {}, {}, {}, {4, 0}, {4, 1}, {{4, 2, null}, null_at(2)}, {5}}; + auto child2 = int32s_col{{null, 2, 3, 4, 3, null, 1, 5}, nulls_at({0, 5})}; + return structs_col{{child0, child1, child2}, null_at(0)}; + }(); + + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + auto const sorted = cudf::sort(cudf::table_view{{input}}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); + } + + { + auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; + auto const expected_sort = [] { + auto child0 = [] { + auto child0 = + int32s_lists{{5}, {{4, 2, null}, null_at(2)}, {4, 1}, {4, 0}, {}, {}, {}, {} /*NULL*/}; + auto child1 = int32s_col{{5, 1, null, 3, 4, 3, 2, null}, nulls_at({2, 7})}; + return structs_col{{child0, child1}, null_at(7)}; + }(); + auto child1 = + int32s_lists{{5}, {{4, 2, null}, null_at(2)}, {4, 1}, {4, 0}, {}, {}, {}, {} /*NULL*/}; + auto child2 = int32s_col{{5, 1, null, 3, 4, 3, 2, null}, nulls_at({2, 7})}; + return structs_col{{child0, child1, child2}, null_at(7)}; + }(); + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); } } -TEST_F(structs_test, ListsHaveStructs) +struct NestedListTest : public cudf::test::BaseFixture { +}; + +TEST_F(NestedListTest, SimpleListsOfStructs) { auto const input = [] { auto const get_structs = [] { @@ -89,4 +326,15 @@ TEST_F(structs_test, ListsHaveStructs) printf("line %d\n", __LINE__); cudf::test::print(sorted->get_column(0).view()); } + + { + auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + + printf("line %d\n", __LINE__); + cudf::test::print(*order); + + auto const sorted = cudf::sort(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + printf("line %d\n", __LINE__); + cudf::test::print(sorted->get_column(0).view()); + } } From 660fa3e0d52e5395aea1fa1f8924e29c0eb7f21f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 7 Mar 2023 14:33:57 -0800 Subject: [PATCH 034/140] Complete unit tests --- cpp/tests/CMakeLists.txt | 7 +- cpp/tests/sort/nested_types_tests.cpp | 340 --------------------- cpp/tests/sort/sort_nested_types_tests.cpp | 257 ++++++++++++++++ 3 files changed, 263 insertions(+), 341 deletions(-) delete mode 100644 cpp/tests/sort/nested_types_tests.cpp create mode 100644 cpp/tests/sort/sort_nested_types_tests.cpp diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 66c57a827b7..e197bfa737b 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -243,7 +243,12 @@ endif() # ################################################################################################## # * sort tests ------------------------------------------------------------------------------------ ConfigureTest( - SORT_TEST sort/nested_types_tests.cpp + SORT_TEST + sort/segmented_sort_tests.cpp + sort/sort_nested_types_tests.cpp + sort/sort_test.cpp + sort/stable_sort_tests.cpp + sort/rank_test.cpp ) # ################################################################################################## diff --git a/cpp/tests/sort/nested_types_tests.cpp b/cpp/tests/sort/nested_types_tests.cpp deleted file mode 100644 index 3f582506bd9..00000000000 --- a/cpp/tests/sort/nested_types_tests.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2023, 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 -#include -#include -#include - -#include - -using int32s_lists = cudf::test::lists_column_wrapper; -using int32s_col = cudf::test::fixed_width_column_wrapper; -using structs_col = cudf::test::structs_column_wrapper; - -using namespace cudf::test::iterators; - -constexpr auto null{0}; - -struct NestedStructTest : public cudf::test::BaseFixture { -}; - -TEST_F(NestedStructTest, SimpleStructsOfListsNoNulls) -{ - auto const input = [] { - auto child = int32s_lists{{4, 2, 0}, {2}, {0, 5}, {1, 5}, {4, 1}}; - return structs_col{{child}}; - }(); - - { - auto const expected_order = int32s_col{2, 3, 1, 4, 0}; - auto const expected_sort = [] { - auto child = int32s_lists{{0, 5}, {1, 5}, {2}, {4, 1}, {4, 2, 0}}; - return structs_col{{child}}; - }(); - - auto const order = cudf::sorted_order(cudf::table_view{{input}}); - auto const sorted = cudf::sort(cudf::table_view{{input}}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } - - { - auto const expected_order = int32s_col{0, 4, 1, 3, 2}; - auto const expected_sort = [] { - auto child = int32s_lists{{4, 2, 0}, {4, 1}, {2}, {1, 5}, {0, 5}}; - return structs_col{{child}}; - }(); - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } -} - -TEST_F(NestedStructTest, SimpleStructsOfListsWithNulls) -{ - auto const input = [] { - auto child = - int32s_lists{{{4, 2, null}, null_at(2)}, {2}, {{null, 5}, null_at(0)}, {0, 5}, {4, 1}}; - return structs_col{{child}}; - }(); - - { - auto const expected_order = int32s_col{2, 3, 1, 4, 0}; - auto const expected_sort = [] { - auto child = - int32s_lists{{{null, 5}, null_at(0)}, {0, 5}, {2}, {4, 1}, {{4, 2, null}, null_at(2)}}; - return structs_col{{child}}; - }(); - - auto const order = cudf::sorted_order(cudf::table_view{{input}}); - auto const sorted = cudf::sort(cudf::table_view{{input}}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } - - { - auto const expected_order = int32s_col{0, 4, 1, 3, 2}; - auto const expected_sort = [] { - auto child = - int32s_lists{{{4, 2, null}, null_at(2)}, {4, 1}, {2}, {0, 5}, {{null, 5}, null_at(0)}}; - return structs_col{{child}}; - }(); - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } -} - -TEST_F(NestedStructTest, StructsHaveListsNoNulls) -{ - auto const input = [] { - auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; - auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; - return structs_col{{child0, child1}}; - }(); - - { - auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; - auto const expected_sort = [] { - auto child0 = int32s_lists{{}, {}, {}, {4, 0}, {4, 1}, {4, 2, 0}, {5}}; - auto child1 = int32s_col{2, 3, 4, 3, 0, 1, 5}; - return structs_col{{child0, child1}}; - }(); - - auto const order = cudf::sorted_order(cudf::table_view{{input}}); - auto const sorted = cudf::sort(cudf::table_view{{input}}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } - - { - auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; - auto const expected_sort = [] { - auto child0 = int32s_lists{{5}, {4, 2, 0}, {4, 1}, {4, 0}, {}, {}, {}}; - auto child1 = int32s_col{5, 1, 0, 3, 4, 3, 2}; - return structs_col{{child0, child1}}; - }(); - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } -} - -TEST_F(NestedStructTest, StructsHaveListsWithNulls) -{ - auto const input = [] { - auto child0 = - int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; - auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; - return structs_col{{child0, child1}, null_at(2)}; - }(); - - { - auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; - auto const expected_sort = [] { - auto child0 = - int32s_lists{{} /*NULL*/, {}, {}, {}, {4, 0}, {4, 1}, {{4, 2, null}, null_at(2)}, {5}}; - auto child1 = int32s_col{{null, 2, 3, 4, 3, null, 1, 5}, nulls_at({0, 5})}; - return structs_col{{child0, child1}, null_at(0)}; - }(); - - auto const order = cudf::sorted_order(cudf::table_view{{input}}); - auto const sorted = cudf::sort(cudf::table_view{{input}}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } - - { - auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; - auto const expected_sort = [] { - auto child0 = - int32s_lists{{5}, {{4, 2, null}, null_at(2)}, {4, 1}, {4, 0}, {}, {}, {}, {} /*NULL*/}; - auto child1 = int32s_col{{5, 1, null, 3, 4, 3, 2, null}, nulls_at({2, 7})}; - return structs_col{{child0, child1}, null_at(7)}; - }(); - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } -} - -TEST_F(NestedStructTest, StructsOfStructsHaveListsNoNulls) -{ - auto const input = [] { - auto child0 = [] { - auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; - auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; - return structs_col{{child0, child1}}; - }(); - auto child1 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; - auto child2 = int32s_col{1, 2, 5, 0, 3, 3, 4}; - return structs_col{{child0, child1, child2}}; - }(); - - { - auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; - auto const expected_sort = [] { - auto child0 = [] { - auto child0 = int32s_lists{{}, {}, {}, {4, 0}, {4, 1}, {4, 2, 0}, {5}}; - auto child1 = int32s_col{2, 3, 4, 3, 0, 1, 5}; - return structs_col{{child0, child1}}; - }(); - auto child1 = int32s_lists{{}, {}, {}, {4, 0}, {4, 1}, {4, 2, 0}, {5}}; - auto child2 = int32s_col{2, 3, 4, 3, 0, 1, 5}; - return structs_col{{child0, child1, child2}}; - }(); - - auto const order = cudf::sorted_order(cudf::table_view{{input}}); - auto const sorted = cudf::sort(cudf::table_view{{input}}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } - - { - auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; - auto const expected_sort = [] { - auto child0 = [] { - auto child0 = int32s_lists{{5}, {4, 2, 0}, {4, 1}, {4, 0}, {}, {}, {}}; - auto child1 = int32s_col{5, 1, 0, 3, 4, 3, 2}; - return structs_col{{child0, child1}}; - }(); - auto child1 = int32s_lists{{5}, {4, 2, 0}, {4, 1}, {4, 0}, {}, {}, {}}; - auto child2 = int32s_col{5, 1, 0, 3, 4, 3, 2}; - return structs_col{{child0, child1, child2}}; - }(); - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } -} - -TEST_F(NestedStructTest, StructsOfStructsHaveListsWithNulls) -{ - auto const input = [] { - auto child0 = [] { - auto child0 = - int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; - auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; - return structs_col{{child0, child1}, null_at(2)}; - }(); - auto child1 = - int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; - auto child2 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; - return structs_col{{child0, child1, child2}, null_at(2)}; - }(); - - { - auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; - auto const expected_sort = [] { - auto child0 = [] { - auto child0 = - int32s_lists{{} /*NULL*/, {}, {}, {}, {4, 0}, {4, 1}, {{4, 2, null}, null_at(2)}, {5}}; - auto child1 = int32s_col{{null, 2, 3, 4, 3, null, 1, 5}, nulls_at({0, 5})}; - return structs_col{{child0, child1}, null_at(0)}; - }(); - auto child1 = - int32s_lists{{} /*NULL*/, {}, {}, {}, {4, 0}, {4, 1}, {{4, 2, null}, null_at(2)}, {5}}; - auto child2 = int32s_col{{null, 2, 3, 4, 3, null, 1, 5}, nulls_at({0, 5})}; - return structs_col{{child0, child1, child2}, null_at(0)}; - }(); - - auto const order = cudf::sorted_order(cudf::table_view{{input}}); - auto const sorted = cudf::sort(cudf::table_view{{input}}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } - - { - auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; - auto const expected_sort = [] { - auto child0 = [] { - auto child0 = - int32s_lists{{5}, {{4, 2, null}, null_at(2)}, {4, 1}, {4, 0}, {}, {}, {}, {} /*NULL*/}; - auto child1 = int32s_col{{5, 1, null, 3, 4, 3, 2, null}, nulls_at({2, 7})}; - return structs_col{{child0, child1}, null_at(7)}; - }(); - auto child1 = - int32s_lists{{5}, {{4, 2, null}, null_at(2)}, {4, 1}, {4, 0}, {}, {}, {}, {} /*NULL*/}; - auto child2 = int32s_col{{5, 1, null, 3, 4, 3, 2, null}, nulls_at({2, 7})}; - return structs_col{{child0, child1, child2}, null_at(7)}; - }(); - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - auto const sorted = cudf::sort(cudf::table_view{{input}}, {cudf::order::DESCENDING}); - - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_sort, sorted->get_column(0).view()); - } -} - -struct NestedListTest : public cudf::test::BaseFixture { -}; - -TEST_F(NestedListTest, SimpleListsOfStructs) -{ - auto const input = [] { - auto const get_structs = [] { - auto child1 = int32s_col{1, 2, 3, 0, 1, 2, 4, 5, 0, 1}; - // auto child2 = int32s_col{11, 12, 13, 10, 11, 12, 14, 15, 10, 11}; - return structs_col{{child1 /*, child2*/}}; - }; - - return cudf::make_lists_column( - 4, int32s_col{0, 3, 6, 8, 10}.release(), get_structs().release(), 0, {}); - }(); - - printf("line %d\n", __LINE__); - cudf::test::print(*input); - - if (1) { - auto const order = cudf::sorted_order(cudf::table_view{{*input}}); - - printf("line %d\n", __LINE__); - cudf::test::print(*order); - - auto const sorted = cudf::sort(cudf::table_view{{*input}}); - - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); - } - - { - auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); - - printf("line %d\n", __LINE__); - cudf::test::print(*order); - - auto const sorted = cudf::sort(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); - printf("line %d\n", __LINE__); - cudf::test::print(sorted->get_column(0).view()); - } -} diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp new file mode 100644 index 00000000000..81b859cb92d --- /dev/null +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2023, 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 +#include +#include +#include + +#include +#include + +using int32s_lists = cudf::test::lists_column_wrapper; +using int32s_col = cudf::test::fixed_width_column_wrapper; +using structs_col = cudf::test::structs_column_wrapper; + +using namespace cudf::test::iterators; + +constexpr auto null{0}; + +struct NestedStructTest : public cudf::test::BaseFixture { +}; + +TEST_F(NestedStructTest, SimpleStructsOfListsNoNulls) +{ + auto const input = [] { + auto child = int32s_lists{{4, 2, 0}, {2}, {0, 5}, {1, 5}, {4, 1}}; + return structs_col{{child}}; + }(); + + { + auto const expected_order = int32s_col{2, 3, 1, 4, 0}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{0, 4, 1, 3, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, SimpleStructsOfListsWithNulls) +{ + auto const input = [] { + auto child = + int32s_lists{{{4, 2, null}, null_at(2)}, {2}, {{null, 5}, null_at(0)}, {0, 5}, {4, 1}}; + return structs_col{{child}}; + }(); + + { + auto const expected_order = int32s_col{2, 3, 1, 4, 0}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{0, 4, 1, 3, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsHaveListsNoNulls) +{ + auto const input = [] { + auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1}}; + }(); + + { + auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsHaveListsWithNulls) +{ + auto const input = [] { + auto child0 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1}, null_at(2)}; + }(); + + { + auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsOfStructsHaveListsNoNulls) +{ + auto const input = [] { + auto child0 = [] { + auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1}}; + }(); + auto child1 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child2 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1, child2}}; + }(); + + { + auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsOfStructsHaveListsWithNulls) +{ + auto const input = [] { + auto child0 = [] { + auto child0 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1}, null_at(2)}; + }(); + auto child1 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child2 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1, child2}, null_at(2)}; + }(); + + { + auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +struct NestedListTest : public cudf::test::BaseFixture { +}; + +TEST_F(NestedListTest, SimpleListsOfStructsNoNulls) +{ + auto const input = [] { + auto const get_structs = [] { + auto child0 = int32s_col{3, 2, 3, 3, 4, 2, 4, 4, 1, 0, 3, 0, 2, 5, 4}; + auto child1 = int32s_col{0, 4, 3, 2, 1, 1, 5, 1, 5, 5, 4, 2, 4, 1, 3}; + return structs_col{{child0, child1}}; + }; + return cudf::make_lists_column( + 8, int32s_col{0, 3, 5, 6, 6, 8, 10, 12, 15}.release(), get_structs().release(), 0, {}); + }(); + + { + auto const expected_order = int32s_col{3, 5, 2, 7, 0, 1, 6, 4}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{4, 6, 1, 0, 7, 2, 5, 3}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedListTest, SimpleListsOfStructsWithNulls) +{ + auto const input = [] { + auto const get_structs = [] { + auto child0 = int32s_col{{null, null, 1, null, 4, 3, null, 5, 5, 1, null, 5, 4}, + nulls_at({0, 1, 3, 6, 10})}; + auto child1 = int32s_col{{2, null, 2, null, 2, 5, 4, 3, 0, 1, 3, 2, 2}, nulls_at({1, 3})}; + return structs_col{{child0, child1}, nulls_at({1, 3})}; + }; + return cudf::make_lists_column( + 8, int32s_col{0, 3, 3, 5, 5, 7, 7, 10, 13}.release(), get_structs().release(), 0, {}); + }(); + + { + auto const expected_order = int32s_col{1, 3, 5, 2, 0, 7, 4, 6}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{6, 4, 7, 0, 2, 1, 3, 5}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedListTest, ListsOfListsOfStructsNoNulls) +{ + auto const input = [] { + auto const get_structs = [] { + auto child0 = int32s_col{0, 7, 4, 9, 2, 9, 4, 1, 5, 5, 3, 7, 0, 6, 3, 1, 9}; + auto child1 = int32s_col{4, 6, 7, 3, 1, 2, 1, 10, 7, 9, 8, 7, 1, 10, 5, 3, 3}; + return structs_col{{child0, child1}}; + }; + auto lists_of_structs = + cudf::make_lists_column(13, + int32s_col{0, 1, 3, 4, 5, 7, 9, 10, 12, 12, 14, 15, 17, 17}.release(), + get_structs().release(), + 0, + {}); + return cudf::make_lists_column( + 8, int32s_col{0, 3, 4, 6, 6, 8, 10, 10, 13}.release(), std::move(lists_of_structs), 0, {}); + }(); + + { + auto const expected_order = int32s_col{3, 6, 5, 0, 1, 7, 4, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{2, 4, 7, 1, 0, 5, 3, 6}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} From 54ffd3478f800e6084ef92604448ea6aa901f9ef Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 7 Mar 2023 14:59:03 -0800 Subject: [PATCH 035/140] Update copyright year --- cpp/include/cudf/detail/sorting.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cudf/detail/sorting.hpp b/cpp/include/cudf/detail/sorting.hpp index 5e9efbd1e30..8f92b66d5fa 100644 --- a/cpp/include/cudf/detail/sorting.hpp +++ b/cpp/include/cudf/detail/sorting.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 8c9bedf7e0754998e0fa235bea8d019694b193db Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 7 Mar 2023 14:59:30 -0800 Subject: [PATCH 036/140] Reformat --- cpp/tests/CMakeLists.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index e197bfa737b..0ebf015f6f9 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -243,12 +243,8 @@ endif() # ################################################################################################## # * sort tests ------------------------------------------------------------------------------------ ConfigureTest( - SORT_TEST - sort/segmented_sort_tests.cpp - sort/sort_nested_types_tests.cpp - sort/sort_test.cpp - sort/stable_sort_tests.cpp - sort/rank_test.cpp + SORT_TEST sort/segmented_sort_tests.cpp sort/sort_nested_types_tests.cpp sort/sort_test.cpp + sort/stable_sort_tests.cpp sort/rank_test.cpp ) # ################################################################################################## From 96e28b8629a129adab8f011b92eae87b93958673 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 8 Mar 2023 10:30:22 -0800 Subject: [PATCH 037/140] Use first rank instead of dense rank --- cpp/src/table/row_operators.cu | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 61b77d81051..8cdec7fdb89 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -379,8 +379,13 @@ std::pair> transform_lists_of_structs( auto const child = input.child(lists_column_view::child_column_index); if (child.type().id() == type_id::STRUCT) { + // Dense ranks can accurately reflect the order of structs: structs compared equal will have + // the same rank values. + // However, first ranks are computed faster and are good enough for ordering them. Structs + // compared equal always have consecutive rank values (in stable order) thus they are still + // sorted correctly by their ranks. auto ranks = cudf::detail::rank(child, - rank_method::DENSE, + rank_method::FIRST, order::ASCENDING, null_policy::EXCLUDE, null_order::BEFORE, From 4e0cf5b0b80170af25db40dfd00a649a41f81c11 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 8 Mar 2023 13:12:48 -0800 Subject: [PATCH 038/140] Cleanup more header --- cpp/include/cudf/detail/groupby/sort_helper.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/include/cudf/detail/groupby/sort_helper.hpp b/cpp/include/cudf/detail/groupby/sort_helper.hpp index 3ef7a7d5acf..b2fee2b7de8 100644 --- a/cpp/include/cudf/detail/groupby/sort_helper.hpp +++ b/cpp/include/cudf/detail/groupby/sort_helper.hpp @@ -18,7 +18,6 @@ #include #include -#include #include #include From 2b5febff52ef61af3652f5d12b67728be3ab61fb Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 8 Mar 2023 14:42:28 -0800 Subject: [PATCH 039/140] Update doxygen Signed-off-by: Nghia Truong --- cpp/include/cudf/table/experimental/row_operators.cuh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 96123021260..f33b2256d72 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -726,7 +726,12 @@ struct preprocessed_table { * contain an empty `dremel_device_view`. As such, this uvector has as many elements as there are * columns in the table (unlike the `dremel_data` parameter, which is only as long as the number * of list columns). - * @param TODO + * @param flattened_input_aux_data The data structure generated from + * `cudf::structs::detail::flatten_nested_columns` that contains additional information used for + * row comparisons. + * @param transformed_structs_columns The intermediate columns resulted from transforming child + * of lists-of-structs columns into lists-of-integers columns and will be used for row + * comparation. */ preprocessed_table( table_device_view_owner&& table, From 74b036bd3cc912835cde1ab307ac412d06627e33 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 9 Mar 2023 09:42:05 -0800 Subject: [PATCH 040/140] Pass `stream` parameter to `flatten_nested_columns` --- cpp/src/table/row_operators.cu | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 8cdec7fdb89..9488d6b0ecf 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -359,7 +359,7 @@ namespace { * @param input The input column to transform * @param stream CUDA stream used for device memory operations and kernel launches * @return A pair of new column_view representing the transformed input and the generated rank - * (integer) column which needs to be kept alive. + * (integer) column which needs to be kept alive */ std::pair> transform_lists_of_structs( column_view const& input, rmm::cuda_stream_view stream) @@ -414,7 +414,7 @@ std::pair> transform_lists_of_structs( * @param input The input table to transform * @param stream CUDA stream used for device memory operations and kernel launches * @return A pair of new table_view representing the transformed input and the generated rank - * (integer) column which needs to be kept alive. + * (integer) column which needs to be kept alive */ std::pair>> transform_lists_of_structs( table_view const& input, rmm::cuda_stream_view stream) @@ -455,22 +455,23 @@ bool has_nested_structs_of_lists(column_view const& input) * a table of non-struct columns. Otherwise, the input table is passed through. * * @param input The input table + * @param stream The stream to launch kernels and h->d copies on while preprocessing * @return A pair of table_view representing the flattened input and an auxiliary data structure - * that needs to be kept alive. + * that needs to be kept alive */ std::pair> flatten_nested_structs_of_lists(table_view const& input, host_span column_order, - host_span null_precedence) + host_span null_precedence, + rmm::cuda_stream_view stream) { if (std::any_of(input.begin(), input.end(), has_nested_structs_of_lists)) { - // TODO: Pass in stream parameter. - // Issue: https://github.com/rapidsai/cudf/issues/12349 auto structs_flattened = cudf::structs::detail::flatten_nested_columns( input, std::vector{column_order.begin(), column_order.end()}, std::vector{null_precedence.begin(), null_precedence.end()}, - cudf::structs::detail::column_nullability::FORCE); + cudf::structs::detail::column_nullability::FORCE, + stream); auto output_table = structs_flattened->flattened_columns(); return {std::move(output_table), std::move(structs_flattened)}; } @@ -488,7 +489,7 @@ std::shared_ptr preprocessed_table::create( { // Firstly, flatten the input table if it contains any structs-of-lists column. auto [flattened_t, flattened_t_aux_data] = - flatten_nested_structs_of_lists(t, column_order, null_precedence); + flatten_nested_structs_of_lists(t, column_order, null_precedence, stream); // Next, transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_t, transformed_aux_data] = transform_lists_of_structs(flattened_t, stream); From 7e874f6683bb75f69b9f1c4f839c874d24dd0785 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 9 Mar 2023 09:58:49 -0800 Subject: [PATCH 041/140] Fix docs --- cpp/include/cudf/table/experimental/row_operators.cuh | 2 +- cpp/src/table/row_operators.cu | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index f33b2256d72..deaa77eb352 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -731,7 +731,7 @@ struct preprocessed_table { * row comparisons. * @param transformed_structs_columns The intermediate columns resulted from transforming child * of lists-of-structs columns into lists-of-integers columns and will be used for row - * comparation. + * comparison. */ preprocessed_table( table_device_view_owner&& table, diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 9488d6b0ecf..be75e7a33cc 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -432,6 +432,7 @@ std::pair>> transform_lists_of_s /** * @brief Check if the input column has structs-of-lists column at any nested level. + * * @param input The input column to check * @return Boolean value indicating if there is structs-of-lists column */ From 9313b4cc5b92502747451904f7d897cc70e0b1d3 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 9 Mar 2023 15:43:57 -0800 Subject: [PATCH 042/140] Fix relevant unit tests --- cpp/tests/groupby/structs_tests.cpp | 12 ++++++-- cpp/tests/structs/utilities_tests.cpp | 42 --------------------------- 2 files changed, 10 insertions(+), 44 deletions(-) diff --git a/cpp/tests/groupby/structs_tests.cpp b/cpp/tests/groupby/structs_tests.cpp index a7bc6016307..57c93db50b5 100644 --- a/cpp/tests/groupby/structs_tests.cpp +++ b/cpp/tests/groupby/structs_tests.cpp @@ -294,11 +294,12 @@ TYPED_TEST(groupby_structs_test, all_null_input) test_sum_agg(keys, values, expected_keys, expected_values); } -TYPED_TEST(groupby_structs_test, lists_are_unsupported) +TYPED_TEST(groupby_structs_test, lists_as_keys) { using V = int32_t; // Type of Aggregation Column. using M0 = int32_t; // Type of STRUCT's first (i.e. 0th) member. using M1 = TypeParam; // Type of STRUCT's second (i.e. 1th) member. + using R = cudf::detail::target_type_t; // clang-format off auto values = fwcw { 0, 1, 2, 3, 4 }; @@ -307,5 +308,12 @@ TYPED_TEST(groupby_structs_test, lists_are_unsupported) // clang-format on auto keys = cudf::test::structs_column_wrapper{{member_0, member_1}}; - EXPECT_THROW(test_sum_agg(keys, values, keys, values), cudf::logic_error); + // clang-format off + auto expected_values = fwcw { 3, 5, 2 }; + auto expected_member_0 = lcw { {1,1}, {2,2}, {3,3} }; + auto expected_member_1 = fwcw{ 1, 2, 3 }; + // clang-format on + auto expected_keys = cudf::test::structs_column_wrapper{{expected_member_0, expected_member_1}}; + + test_sum_agg(keys, values, expected_keys, expected_values); } diff --git a/cpp/tests/structs/utilities_tests.cpp b/cpp/tests/structs/utilities_tests.cpp index e92b96553c0..88bdeca8fbb 100644 --- a/cpp/tests/structs/utilities_tests.cpp +++ b/cpp/tests/structs/utilities_tests.cpp @@ -60,26 +60,6 @@ TYPED_TEST(TypedStructUtilitiesTest, ListsAtTopLevel) CUDF_TEST_EXPECT_TABLES_EQUAL(table, flattened_table->flattened_columns()); } -TYPED_TEST(TypedStructUtilitiesTest, NestedListsUnsupported) -{ - using T = TypeParam; - using lists = cudf::test::lists_column_wrapper; - using nums = cudf::test::fixed_width_column_wrapper; - - auto lists_member = lists{{0, 1}, {22, 33}, {44, 55, 66}}; - auto nums_member = nums{{0, 1, 2}, cudf::test::iterators::null_at(6)}; - auto structs_col = cudf::test::structs_column_wrapper{{nums_member, lists_member}}; - auto nums_col = nums{{0, 1, 2}, cudf::test::iterators::null_at(6)}; - - EXPECT_THROW((void)cudf::structs::detail::flatten_nested_columns( - cudf::table_view{{nums_col, structs_col}}, - {}, - {}, - cudf::structs::detail::column_nullability::FORCE, - cudf::get_default_stream()), - cudf::logic_error); -} - TYPED_TEST(TypedStructUtilitiesTest, NoStructs) { using T = TypeParam; @@ -312,28 +292,6 @@ TYPED_TEST(TypedStructUtilitiesTest, StructOfStructWithNullsAtAllLevels) CUDF_TEST_EXPECT_TABLES_EQUAL(expected, flattened_table->flattened_columns()); } -TYPED_TEST(TypedStructUtilitiesTest, ListsAreUnsupported) -{ - using T = TypeParam; - using ints = cudf::test::fixed_width_column_wrapper; - using lcw = cudf::test::lists_column_wrapper; - - // clang-format off - auto lists_member = lcw{ {0,1,2}, {3,4,5}, {6,7,8,9} }; - auto ints_member = ints{ 0, 1, 2 }; - // clang-format on - - auto structs_with_lists_col = cudf::test::structs_column_wrapper{lists_member, ints_member}; - - EXPECT_THROW((void)cudf::structs::detail::flatten_nested_columns( - cudf::table_view{{structs_with_lists_col}}, - {}, - {}, - cudf::structs::detail::column_nullability::FORCE, - cudf::get_default_stream()), - cudf::logic_error); -} - struct SuperimposeTest : StructUtilitiesTest { }; From 1cf20a227ff1f7764c1a75938faa836d203dc093 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 13 Mar 2023 12:39:24 -0700 Subject: [PATCH 043/140] Add sort lists of structs benchmark --- cpp/benchmarks/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index cc0b642a337..d30035a16ba 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -170,8 +170,8 @@ ConfigureNVBench(SEARCH_NVBENCH search/contains.cpp) # * sort benchmark -------------------------------------------------------------------------------- ConfigureBench(SORT_BENCH sort/rank.cpp sort/sort.cpp sort/sort_strings.cpp) ConfigureNVBench( - SORT_NVBENCH sort/rank_lists.cpp sort/rank_structs.cpp sort/segmented_sort.cpp - sort/sort_lists.cpp sort/sort_structs.cpp + SORT_NVBENCH + sort/sort_lists.cpp ) # ################################################################################################## From 55682f3d635edda8700bbf86bba338469b6434f2 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 13 Mar 2023 13:17:39 -0700 Subject: [PATCH 044/140] Add benchmark code --- cpp/benchmarks/sort/nested_types_common.hpp | 12 +++- cpp/benchmarks/sort/sort_lists.cpp | 70 ++++++++++++++++++++- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/cpp/benchmarks/sort/nested_types_common.hpp b/cpp/benchmarks/sort/nested_types_common.hpp index fabef3a7a51..e0626b1b96f 100644 --- a/cpp/benchmarks/sort/nested_types_common.hpp +++ b/cpp/benchmarks/sort/nested_types_common.hpp @@ -28,17 +28,23 @@ #include -inline std::unique_ptr create_lists_data(nvbench::state& state) +inline std::unique_ptr create_lists_data(nvbench::state& state, + cudf::size_type const num_columns = 1, + cudf::size_type const min_val = 0, + cudf::size_type const max_val = 5) { const size_t size_bytes(state.get_int64("size_bytes")); const cudf::size_type depth{static_cast(state.get_int64("depth"))}; auto const null_frequency{state.get_float64("null_frequency")}; data_profile table_profile; - table_profile.set_distribution_params(cudf::type_id::LIST, distribution_id::UNIFORM, 0, 5); + table_profile.set_distribution_params( + cudf::type_id::LIST, distribution_id::UNIFORM, min_val, max_val); table_profile.set_list_depth(depth); table_profile.set_null_probability(null_frequency); - return create_random_table({cudf::type_id::LIST}, table_size_bytes{size_bytes}, table_profile); + return create_random_table(std::vector(num_columns, cudf::type_id::LIST), + table_size_bytes{size_bytes}, + table_profile); } inline std::unique_ptr create_structs_data(nvbench::state& state, diff --git a/cpp/benchmarks/sort/sort_lists.cpp b/cpp/benchmarks/sort/sort_lists.cpp index b55b60f5ec9..da2785fac55 100644 --- a/cpp/benchmarks/sort/sort_lists.cpp +++ b/cpp/benchmarks/sort/sort_lists.cpp @@ -20,18 +20,82 @@ #include -void nvbench_sort_lists(nvbench::state& state) +namespace { +auto constexpr min_val = 0; +auto constexpr max_val = 100; + +void sort_multiple_lists(nvbench::state& state) +{ + auto const num_columns = static_cast(state.get_int64("num_columns")); + auto const input_table = create_lists_data(state, num_columns, min_val, max_val); + auto const stream = cudf::get_default_stream(); + + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + cudf::detail::sorted_order( + *input_table, {}, {}, stream, rmm::mr::get_current_device_resource()); + }); +} + +void sort_lists_of_structs(nvbench::state& state) { - auto const table = create_lists_data(state); + auto const num_columns = static_cast(state.get_int64("num_columns")); + auto const lists_table = create_lists_data(state, num_columns, min_val, max_val); + // After having a table of (multile) lists columns, convert those lists columns into lists of + // structs columns. The children of these structs columns are also children of the original lists + // columns. + // Such resulted lists-of-structs columns are very similar to the original lists-of-integers + // columns so their benchmarks can be somewhat comparable. + std::vector lists_of_structs; + for (auto const& col : lists_table->view()) { + auto const child = col.child(cudf::lists_column_view::child_column_index); + + // Put the child column under a struct column having the same null mask/null count. + auto const new_child = cudf::column_view{cudf::data_type{cudf::type_id::STRUCT}, + child.size(), + nullptr, + child.null_mask(), + child.null_count(), + child.offset(), + {child}}; + auto const converted_col = + cudf::column_view{cudf::data_type{cudf::type_id::LIST}, + col.size(), + nullptr, + col.null_mask(), + col.null_count(), + col.offset(), + {col.child(cudf::lists_column_view::offsets_column_index), new_child}}; + lists_of_structs.push_back(converted_col); + } + + auto const input_table = cudf::table_view{lists_of_structs}; + auto const stream = cudf::get_default_stream(); + + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { rmm::cuda_stream_view stream_view{launch.get_stream()}; - cudf::detail::sorted_order(*table, {}, {}, stream_view, rmm::mr::get_current_device_resource()); + cudf::detail::sorted_order(input_table, {}, {}, stream, rmm::mr::get_current_device_resource()); }); } +} // namespace + +void nvbench_sort_lists(nvbench::state& state) +{ + const auto has_lists_of_structs = state.get_int64("lists_of_structs") > 0; + if (has_lists_of_structs) { + sort_lists_of_structs(state); + } else { + sort_multiple_lists(state); + } +} + NVBENCH_BENCH(nvbench_sort_lists) .set_name("sort_list") .add_int64_power_of_two_axis("size_bytes", {10, 18, 24, 28}) .add_int64_axis("depth", {1, 4}) + .add_int64_axis("num_columns", {1}) + .add_int64_axis("lists_of_structs", {0, 1}) .add_float64_axis("null_frequency", {0, 0.2}); From d445652376dd861825222de1fcd6421a50819585 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 13 Mar 2023 13:17:46 -0700 Subject: [PATCH 045/140] Revert "Add sort lists of structs benchmark" This reverts commit 1cf20a227ff1f7764c1a75938faa836d203dc093. --- cpp/benchmarks/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index d30035a16ba..cc0b642a337 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -170,8 +170,8 @@ ConfigureNVBench(SEARCH_NVBENCH search/contains.cpp) # * sort benchmark -------------------------------------------------------------------------------- ConfigureBench(SORT_BENCH sort/rank.cpp sort/sort.cpp sort/sort_strings.cpp) ConfigureNVBench( - SORT_NVBENCH - sort/sort_lists.cpp + SORT_NVBENCH sort/rank_lists.cpp sort/rank_structs.cpp sort/segmented_sort.cpp + sort/sort_lists.cpp sort/sort_structs.cpp ) # ################################################################################################## From 4e36b15e9357936f139d84d07587df54b605cefe Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 13 Mar 2023 13:26:54 -0700 Subject: [PATCH 046/140] Fix spell --- cpp/benchmarks/sort/sort_lists.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/benchmarks/sort/sort_lists.cpp b/cpp/benchmarks/sort/sort_lists.cpp index da2785fac55..7927473ac12 100644 --- a/cpp/benchmarks/sort/sort_lists.cpp +++ b/cpp/benchmarks/sort/sort_lists.cpp @@ -42,7 +42,7 @@ void sort_lists_of_structs(nvbench::state& state) auto const num_columns = static_cast(state.get_int64("num_columns")); auto const lists_table = create_lists_data(state, num_columns, min_val, max_val); - // After having a table of (multile) lists columns, convert those lists columns into lists of + // After having a table of (multiple) lists columns, convert those lists columns into lists of // structs columns. The children of these structs columns are also children of the original lists // columns. // Such resulted lists-of-structs columns are very similar to the original lists-of-integers From 1f8ea8feb9326433cd8e5f875a1d17da79f3683f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 15 Mar 2023 14:07:09 -0700 Subject: [PATCH 047/140] Draft implementation Signed-off-by: Nghia Truong --- .../cudf/table/experimental/row_operators.cuh | 49 +++- cpp/src/table/row_operators.cu | 243 +++++++++++++----- 2 files changed, 227 insertions(+), 65 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 394d5686fe6..d85e22e5a0e 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -684,7 +684,12 @@ struct preprocessed_table { * @brief Preprocess table for use with lexicographical comparison * * Sets up the table for use with lexicographical comparison. The resulting preprocessed table can - * be passed to the constructor of `lexicographic::self_comparator` to avoid preprocessing again. + * be passed to the constructor of `lexicographic::self_comparator` or + * `lexicographic::two_table_comparator` to avoid preprocessing again. + * + * Note that the output of this factory function cannot be used in `two_table_comparator` if the + * input table contains lists-of-structs. In such cases, please use the overload + * `preprocessed_table::create(table_view const&, table_view const&,...)`. * * @param table The table to preprocess * @param column_order Optional, host array the same length as a row that indicates the desired @@ -701,6 +706,34 @@ struct preprocessed_table { host_span null_precedence, rmm::cuda_stream_view stream); + /** + * @brief Preprocess tables for use with lexicographical comparison + * + * Sets up the tables for use with lexicographical comparison. The resulting preprocessed tables + * can be passed to the constructor of `lexicographic::self_comparator` or + * `lexicographic::two_table_comparator` to avoid preprocessing again. + * + * This factory function performs some extra operations to guarantee that its output can be safely + * used in `two_table_comparator` for all cases. + * + * @param lhs The lhs table to preprocess + * @param rhs The rhs table to preprocess + * @param column_order Optional, host array the same length as a row that indicates the desired + * ascending/descending order of each column in a row. If empty, it is assumed all columns are + * sorted in ascending order. + * @param null_precedence Optional, device array the same length as a row and indicates how null + * values compare to all other for every column. If it is nullptr, then null precedence would be + * `null_order::BEFORE` for all columns. + * @param stream The stream to launch kernels and h->d copies on while preprocessing. + * @return A pair of shared pointers to the preprocessed tables + */ + static std::pair, std::shared_ptr> create( + table_view const& lhs, + table_view const& rhs, + host_span column_order, + host_span null_precedence, + rmm::cuda_stream_view stream); + private: friend class self_comparator; ///< Allow self_comparator to access private members friend class two_table_comparator; ///< Allow two_table_comparator to access private members @@ -732,6 +765,8 @@ struct preprocessed_table { * @param transformed_structs_columns The intermediate columns resulted from transforming child * of lists-of-structs columns into lists-of-integers columns and will be used for row * comparison. + * @param safe_for_two_table_comparator Flat indicating if the preprocessed table is safe to use + * for two-table comparator. */ preprocessed_table( table_device_view_owner&& table, @@ -741,7 +776,8 @@ struct preprocessed_table { std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns); + std::vector>&& transformed_structs_columns, + bool safe_for_two_table_comparator); preprocessed_table( table_device_view_owner&& table, @@ -749,7 +785,8 @@ struct preprocessed_table { rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns); + std::vector>&& transformed_structs_columns, + bool safe_for_two_table_comparator); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -822,6 +859,9 @@ struct preprocessed_table { // Auxiliary data generated from transforming lists-of-structs into lists-of-integer // that needs to be kept alive. std::vector> _transformed_structs_aux_data; + + // A flag indicating if the preprocessed table is safe to use for two-table comparator. + bool const _safe_for_two_table_comparator; }; /** @@ -1024,6 +1064,9 @@ class two_table_comparator { std::shared_ptr right) : d_left_table{std::move(left)}, d_right_table{std::move(right)} { + CUDF_EXPECTS( + d_left_table->_safe_for_two_table_comparator && d_right_table->_safe_for_two_table_comparator, + "The pre-generated preprocessed_table(s) are not be able to use for two_table_comparator."); } /** diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index be75e7a33cc..90594a53917 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include #include @@ -357,14 +359,18 @@ namespace { * level, the input will be passed through. * * @param input The input column to transform + * @param * @param stream CUDA stream used for device memory operations and kernel launches * @return A pair of new column_view representing the transformed input and the generated rank * (integer) column which needs to be kept alive */ -std::pair> transform_lists_of_structs( - column_view const& input, rmm::cuda_stream_view stream) +std:: + tuple, std::unique_ptr, std::unique_ptr> + transform_lists_of_structs(column_view const& lhs, + std::optional const& rhs, + rmm::cuda_stream_view stream) { - auto const make_transformed_input = [&](auto const& new_child) { + auto const make_transformed_input = [&](auto const& input, auto const& new_child) { return column_view{data_type{type_id::LIST}, input.size(), nullptr, @@ -374,60 +380,115 @@ std::pair> transform_lists_of_structs( {input.child(lists_column_view::offsets_column_index), new_child}}; }; - if (input.type().id() == type_id::LIST) { + auto const default_mr = rmm::mr::get_current_device_resource(); + + if (lhs.type().id() == type_id::LIST) { // Should not use sliced child because we reuse the input's offset value and offsets child. - auto const child = input.child(lists_column_view::child_column_index); - - if (child.type().id() == type_id::STRUCT) { - // Dense ranks can accurately reflect the order of structs: structs compared equal will have - // the same rank values. - // However, first ranks are computed faster and are good enough for ordering them. Structs - // compared equal always have consecutive rank values (in stable order) thus they are still - // sorted correctly by their ranks. - auto ranks = cudf::detail::rank(child, - rank_method::FIRST, - order::ASCENDING, - null_policy::EXCLUDE, - null_order::BEFORE, - false, - stream, - rmm::mr::get_current_device_resource()); - auto transformed_input = make_transformed_input(ranks->view()); - return {std::move(transformed_input), std::move(ranks)}; - } else if (child.type().id() == type_id::LIST) { - auto [new_child, ranks_cols] = transform_lists_of_structs(child, stream); - if (ranks_cols) { - auto transformed_input = make_transformed_input(new_child); - return {transformed_input, std::move(ranks_cols)}; + auto const child_lhs = lhs.child(lists_column_view::child_column_index); + auto const child_rhs = + rhs ? std::optional{rhs.child(lists_column_view::child_column_index)} + : std::nullopt; + + if (child_lhs.type().id() == type_id::STRUCT) { + if (child_rhs) { + auto child_lhs_rhs = std::move( + cudf::detail::concatenate( + /*std::vector*/ {child_lhs, child_rhs.value()}, stream, default_mr) + ->release() + .front()); + + // Dense ranks should be used because we are ranking two separate columns concatenating + // together. + auto const ranks = cudf::detail::rank(child_lhs_rhs->view(), + rank_method::DENSE, + order::ASCENDING, + null_policy::EXCLUDE, + null_order::BEFORE, + false /*percentage*/, + stream, + default_mr); + auto const ranks_slices = cudf::detail::slice( + ranks, + {0, child_lhs.size(), child_lhs.size(), child_lhs.size() + child_rhs.value().size()}, + stream); + auto child_lhs_ranks = std::make_unique(ranks_slices.front()); + auto child_rhs_ranks = std::make_unique(ranks_slices.back()); + auto transformed_lhs = make_transformed_input(lhs, child_lhs_ranks->view()); + auto transformed_rhs = make_transformed_input(rhs, child_rhs_ranks->view()); + return { + transformed_lhs, transformed_rhs, std::move(child_lhs_ranks), std::move(child_rhs_ranks)}; + } else { + // Dense ranks can accurately reflect the order of structs: structs compared equal will have + // the same rank values. + // However, first ranks are computed faster and are good enough for ordering them. Structs + // compared equal always have consecutive rank values (in stable order) thus they are still + // sorted correctly by their ranks. + auto child_lhs_ranks = cudf::detail::rank(child_lhs, + rank_method::FIRST, + order::ASCENDING, + null_policy::EXCLUDE, + null_order::BEFORE, + false /*percentage*/, + stream, + default_mr); + auto transformed_lhs = make_transformed_input(lhs, child_lhs_ranks->view()); + return {std::move(transformed_lhs), std::nullopt, std::move(child_lhs_ranks), nullptr}; + } + } else if (child_lhs.type().id() == type_id::LIST) { + auto [new_child_lhs, new_child_rhs, child_lhs_ranks, child_rhs_ranks] = + transform_lists_of_structs(child_lhs, child_rhs, stream); + if (child_lhs_ranks) { + auto transformed_lhs = make_transformed_input(lhs, new_child_lhs); + auto transformed_rhs = make_transformed_input(rhs, new_child_rhs); + return { + transformed_lhs, transformed_rhs, std::move(child_lhs_ranks), std::move(child_rhs_ranks)}; } } - } else if (input.type().id() == type_id::STRUCT) { + } else if (lhs.type().id() == type_id::STRUCT) { CUDF_UNREACHABLE("Structs columns should be flattened before calling this function."); } - return {input, nullptr}; + return {lhs, rhs, nullptr, nullptr}; } /** * @brief Transform any lists-of-structs column in a given table into lists-of-integers column. * + * If the rhs table is specified, its shape should be pre-checked to match with lhs through + * `check_shape_compatibility`. + * * @param input The input table to transform + * @param tba * @param stream CUDA stream used for device memory operations and kernel launches * @return A pair of new table_view representing the transformed input and the generated rank * (integer) column which needs to be kept alive */ -std::pair>> transform_lists_of_structs( - table_view const& input, rmm::cuda_stream_view stream) +std::tuple, + std::vector>, + std::vector>> +transform_lists_of_structs(table_view const& lhs, + std::optional const& rhs, + rmm::cuda_stream_view stream) { - std::vector transformed_columns; - std::vector> aux_data; - std::for_each(input.begin(), input.end(), [&](auto const& col) { - auto [transformed_col, col_aux_data] = transform_lists_of_structs(col, stream); - transformed_columns.push_back(transformed_col); - if (col_aux_data) { aux_data.emplace_back(std::move(col_aux_data)); } + std::vector transformed_lhs_cols; + std::vector transformed_rhs_cols; + std::vector> lhs_aux_cols; + std::vector> rhs_aux_cols; + for (size_type child_idx = 0; child_idx < lhs.num_columns(); ++child_idx) { + auto const& col = lhs.column(child_idx); + auto [transformed_lhs, transformed_rhs_opt, lhs_aux_data] = + transform_lists_of_structs(col, rhs ? rhs.value().column(child_idx) : std::nullopt, stream); + transformed_lhs_cols.push_back(transformed_lhs); + if (rhs) { transformed_rhs_cols.push_back(transformed_rhs_opt.value()); } + if (lhs_aux_data) { lhs_aux_cols.emplace_back(std::move(lhs_aux_data)); } + if (rhs_aux_data) { rhs_aux_cols.emplace_back(std::move(rhs_aux_data)); } }); - return {table_view{transformed_columns}, std::move(aux_data)}; + return {table_view{transformed_lhs_cols}, + rhs ? table_view{transformed_rhs_cols} : std::nullopt, + std::move(lhs_aux_cols), + std::move(rhs_aux_cols)}; } /** @@ -480,35 +541,28 @@ flatten_nested_structs_of_lists(table_view const& input, return {input, nullptr}; } -} // namespace - -std::shared_ptr preprocessed_table::create( - table_view const& t, +std::shared_ptr create_preprocessed_table( + table_view const& input, + std::unique_ptr&& flattened_input_aux_data, + std::vector>&& transformed_aux_data, host_span column_order, host_span null_precedence, - rmm::cuda_stream_view stream) + bool safe_for_two_table_comparator) { - // Firstly, flatten the input table if it contains any structs-of-lists column. - auto [flattened_t, flattened_t_aux_data] = - flatten_nested_structs_of_lists(t, column_order, null_precedence, stream); - - // Next, transform any (nested) lists-of-structs column into lists-of-integers column. - auto [transformed_t, transformed_aux_data] = transform_lists_of_structs(flattened_t, stream); - - check_lex_compatibility(transformed_t); + check_lex_compatibility(input); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = flattened_t_aux_data ? decompose_structs( - transformed_t, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) - : decompose_structs(transformed_t, column_order, null_precedence); + input, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) + : decompose_structs(input, column_order, null_precedence); auto d_t = table_device_view::create(verticalized_lhs, stream); auto d_column_order = detail::make_device_uvector_async(new_column_order, stream); auto d_null_precedence = detail::make_device_uvector_async(new_null_precedence, stream); auto d_depths = detail::make_device_uvector_async(verticalized_col_depths, stream); - if (detail::has_nested_columns(transformed_t)) { + if (detail::has_nested_columns(input)) { auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(verticalized_lhs, stream); return std::shared_ptr( new preprocessed_table(std::move(d_t), @@ -518,7 +572,8 @@ std::shared_ptr preprocessed_table::create( std::move(dremel_data), std::move(d_dremel_device_view), std::move(flattened_t_aux_data), - std::move(transformed_aux_data))); + std::move(transformed_aux_data), + safe_for_two_table_comparator)); } else { return std::shared_ptr( new preprocessed_table(std::move(d_t), @@ -526,10 +581,70 @@ std::shared_ptr preprocessed_table::create( std::move(d_null_precedence), std::move(d_depths), std::move(flattened_t_aux_data), - std::move(transformed_aux_data))); + std::move(transformed_aux_data), + safe_for_two_table_comparator)); } } +} // namespace + +std::shared_ptr preprocessed_table::create( + table_view const& t, + host_span column_order, + host_span null_precedence, + rmm::cuda_stream_view stream) +{ + // Firstly, flatten the input table if it contains any structs-of-lists column. + auto [flattened_t, flattened_t_aux_data] = + flatten_nested_structs_of_lists(t, column_order, null_precedence, stream); + + // Next, transform any (nested) lists-of-structs column into lists-of-integers column. + [[maybe_unused]] auto [transformed_t, unused_0, transformed_aux_data, unused_1] = + transform_lists_of_structs(flattened_t, std::nullopt, stream); + + // Since the preprocessed_table is created alone, it is safe for two-table comparator + // only if not any transformation for lists-of-structs was performed. + bool const safe_for_two_table_comparator = transformed_aux_data.size() == 0; + + return create_preprocessed_table(transformed_t, + std::move(flattened_t_aux_data), + std::move(transformed_aux_data), + column_order, + null_precedence, + safe_for_two_table_comparator); +} + +std::pair, std::shared_ptr> +preprocessed_table::create(table_view const& lhs, + table_view const& rhs, + host_span column_order, + host_span null_precedence, + rmm::cuda_stream_view stream) +{ + // Firstly, flatten the input table if it contains any structs-of-lists column. + auto [flattened_lhs, flattened_lhs_aux_data] = + flatten_nested_structs_of_lists(lhs, column_order, null_precedence, stream); + auto [flattened_rhs, flattened_rhs_aux_data] = + flatten_nested_structs_of_lists(rhs, column_order, null_precedence, stream); + + // Next, transform any (nested) lists-of-structs column into lists-of-integers column. + auto [transformed_lhs, transformed_rhs_opt, transformed_aux_lhs, transformed_aux_rhs] = + transform_lists_of_structs(flattened_lhs, flattened_rhs, stream); + + return {create_preprocessed_table(transformed_lhs, + std::move(flattened_lhs_aux_data), + std::move(transformed_aux_lhs), + column_order, + null_precedence, + true /*safe_for_two_table_comparator*/), + create_preprocessed_table(transformed_rhs_opt.value(), + std::move(flattened_rhs_aux_data), + std::move(transformed_aux_rhs), + column_order, + null_precedence, + true /*safe_for_two_table_comparator*/)}; +} + preprocessed_table::preprocessed_table( table_device_view_owner&& table, rmm::device_uvector&& column_order, @@ -538,7 +653,8 @@ preprocessed_table::preprocessed_table( std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns) + std::vector>&& transformed_structs_columns, + bool safe_for_two_table_comparator) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -546,7 +662,8 @@ preprocessed_table::preprocessed_table( _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), _flattened_input_aux_data(std::move(flattened_input_aux_data)), - _transformed_structs_aux_data(std::move(transformed_structs_columns)) + _transformed_structs_aux_data(std::move(transformed_structs_columns)), + _safe_for_two_table_comparator(safe_for_two_table_comparator) { } @@ -556,7 +673,8 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns) + std::vector>&& transformed_structs_columns, + bool safe_for_two_table_comparator) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -564,7 +682,8 @@ preprocessed_table::preprocessed_table( _dremel_data{}, _dremel_device_views{}, _flattened_input_aux_data(std::move(flattened_input_aux_data)), - _transformed_structs_aux_data(std::move(transformed_structs_columns)) + _transformed_structs_aux_data(std::move(transformed_structs_columns)), + _safe_for_two_table_comparator(safe_for_two_table_comparator) { } @@ -573,10 +692,10 @@ two_table_comparator::two_table_comparator(table_view const& left, host_span column_order, host_span null_precedence, rmm::cuda_stream_view stream) - : d_left_table{preprocessed_table::create(left, column_order, null_precedence, stream)}, - d_right_table{preprocessed_table::create(right, column_order, null_precedence, stream)} { check_shape_compatibility(left, right); + std::tie(d_left_table, d_right_table) = + preprocessed_table::create(left, right, column_order, null_precedence, stream); } } // namespace lexicographic From e6c5dd2b8b1631260213a56e1fe54999a083ab52 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 15 Mar 2023 14:41:28 -0700 Subject: [PATCH 048/140] Fix compile errors Signed-off-by: Nghia Truong --- .../cudf/table/experimental/row_operators.cuh | 19 +++++++ cpp/src/table/row_operators.cu | 57 +++++++++++-------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index d85e22e5a0e..d4018f43cac 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -738,6 +738,25 @@ struct preprocessed_table { friend class self_comparator; ///< Allow self_comparator to access private members friend class two_table_comparator; ///< Allow two_table_comparator to access private members + /** + * @brief create_preprocessed_table + * @param input + * @param flattened_input_aux_data + * @param transformed_aux_data + * @param column_order + * @param null_precedence + * @param safe_for_two_table_comparator + * @param stream + */ + static std::shared_ptr create_preprocessed_table( + table_view const& input, + std::unique_ptr&& flattened_input_aux_data, + std::vector>&& transformed_aux_data, + host_span column_order, + host_span null_precedence, + bool safe_for_two_table_comparator, + rmm::cuda_stream_view stream); + /** * @brief Construct a preprocessed table for use with lexicographical comparison * diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 90594a53917..00c7528e1ed 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -386,16 +386,15 @@ std:: // Should not use sliced child because we reuse the input's offset value and offsets child. auto const child_lhs = lhs.child(lists_column_view::child_column_index); auto const child_rhs = - rhs ? std::optional{rhs.child(lists_column_view::child_column_index)} + rhs ? std::optional{rhs.value().child(lists_column_view::child_column_index)} : std::nullopt; if (child_lhs.type().id() == type_id::STRUCT) { if (child_rhs) { - auto child_lhs_rhs = std::move( - cudf::detail::concatenate( - /*std::vector*/ {child_lhs, child_rhs.value()}, stream, default_mr) - ->release() - .front()); + auto child_lhs_rhs = cudf::detail::concatenate( + /*std::vector*/ std::vector{child_lhs, child_rhs.value()}, + stream, + default_mr); // Dense ranks should be used because we are ranking two separate columns concatenating // together. @@ -408,13 +407,13 @@ std:: stream, default_mr); auto const ranks_slices = cudf::detail::slice( - ranks, + ranks->view(), {0, child_lhs.size(), child_lhs.size(), child_lhs.size() + child_rhs.value().size()}, stream); auto child_lhs_ranks = std::make_unique(ranks_slices.front()); auto child_rhs_ranks = std::make_unique(ranks_slices.back()); auto transformed_lhs = make_transformed_input(lhs, child_lhs_ranks->view()); - auto transformed_rhs = make_transformed_input(rhs, child_rhs_ranks->view()); + auto transformed_rhs = make_transformed_input(rhs.value(), child_rhs_ranks->view()); return { transformed_lhs, transformed_rhs, std::move(child_lhs_ranks), std::move(child_rhs_ranks)}; } else { @@ -435,11 +434,11 @@ std:: return {std::move(transformed_lhs), std::nullopt, std::move(child_lhs_ranks), nullptr}; } } else if (child_lhs.type().id() == type_id::LIST) { - auto [new_child_lhs, new_child_rhs, child_lhs_ranks, child_rhs_ranks] = + auto [new_child_lhs, new_child_rhs_opt, child_lhs_ranks, child_rhs_ranks] = transform_lists_of_structs(child_lhs, child_rhs, stream); if (child_lhs_ranks) { auto transformed_lhs = make_transformed_input(lhs, new_child_lhs); - auto transformed_rhs = make_transformed_input(rhs, new_child_rhs); + auto transformed_rhs = make_transformed_input(rhs.value(), new_child_rhs_opt.value()); return { transformed_lhs, transformed_rhs, std::move(child_lhs_ranks), std::move(child_rhs_ranks)}; } @@ -477,16 +476,19 @@ transform_lists_of_structs(table_view const& lhs, std::vector> rhs_aux_cols; for (size_type child_idx = 0; child_idx < lhs.num_columns(); ++child_idx) { auto const& col = lhs.column(child_idx); - auto [transformed_lhs, transformed_rhs_opt, lhs_aux_data] = - transform_lists_of_structs(col, rhs ? rhs.value().column(child_idx) : std::nullopt, stream); + auto [transformed_lhs, transformed_rhs_opt, lhs_aux_data, rhs_aux_data] = + transform_lists_of_structs( + col, + rhs ? std::optional{rhs.value().column(child_idx)} : std::nullopt, + stream); transformed_lhs_cols.push_back(transformed_lhs); if (rhs) { transformed_rhs_cols.push_back(transformed_rhs_opt.value()); } if (lhs_aux_data) { lhs_aux_cols.emplace_back(std::move(lhs_aux_data)); } if (rhs_aux_data) { rhs_aux_cols.emplace_back(std::move(rhs_aux_data)); } - }); + } return {table_view{transformed_lhs_cols}, - rhs ? table_view{transformed_rhs_cols} : std::nullopt, + rhs ? std::optional{table_view{transformed_rhs_cols}} : std::nullopt, std::move(lhs_aux_cols), std::move(rhs_aux_cols)}; } @@ -541,20 +543,23 @@ flatten_nested_structs_of_lists(table_view const& input, return {input, nullptr}; } -std::shared_ptr create_preprocessed_table( +} // namespace + +std::shared_ptr preprocessed_table::create_preprocessed_table( table_view const& input, std::unique_ptr&& flattened_input_aux_data, std::vector>&& transformed_aux_data, host_span column_order, host_span null_precedence, - bool safe_for_two_table_comparator) + bool safe_for_two_table_comparator, + rmm::cuda_stream_view stream) { check_lex_compatibility(input); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = - flattened_t_aux_data + flattened_input_aux_data ? decompose_structs( - input, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) + input, flattened_input_aux_data->orders(), flattened_input_aux_data->null_orders()) : decompose_structs(input, column_order, null_precedence); auto d_t = table_device_view::create(verticalized_lhs, stream); @@ -571,7 +576,7 @@ std::shared_ptr create_preprocessed_table( std::move(d_depths), std::move(dremel_data), std::move(d_dremel_device_view), - std::move(flattened_t_aux_data), + std::move(flattened_input_aux_data), std::move(transformed_aux_data), safe_for_two_table_comparator)); } else { @@ -580,14 +585,12 @@ std::shared_ptr create_preprocessed_table( std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), - std::move(flattened_t_aux_data), + std::move(flattened_input_aux_data), std::move(transformed_aux_data), safe_for_two_table_comparator)); } } -} // namespace - std::shared_ptr preprocessed_table::create( table_view const& t, host_span column_order, @@ -611,7 +614,8 @@ std::shared_ptr preprocessed_table::create( std::move(transformed_aux_data), column_order, null_precedence, - safe_for_two_table_comparator); + safe_for_two_table_comparator, + stream); } std::pair, std::shared_ptr> @@ -636,13 +640,16 @@ preprocessed_table::create(table_view const& lhs, std::move(transformed_aux_lhs), column_order, null_precedence, - true /*safe_for_two_table_comparator*/), + true /*safe_for_two_table_comparator*/ + , + stream), create_preprocessed_table(transformed_rhs_opt.value(), std::move(flattened_rhs_aux_data), std::move(transformed_aux_rhs), column_order, null_precedence, - true /*safe_for_two_table_comparator*/)}; + true /*safe_for_two_table_comparator*/, + stream)}; } preprocessed_table::preprocessed_table( From 98ec7d259a6402480928006814da1a96724de144 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 15 Mar 2023 14:55:03 -0700 Subject: [PATCH 049/140] Fix optional access Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 00c7528e1ed..67bfabd8d98 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -390,7 +390,7 @@ std:: : std::nullopt; if (child_lhs.type().id() == type_id::STRUCT) { - if (child_rhs) { + if (rhs) { auto child_lhs_rhs = cudf::detail::concatenate( /*std::vector*/ std::vector{child_lhs, child_rhs.value()}, stream, @@ -436,9 +436,11 @@ std:: } else if (child_lhs.type().id() == type_id::LIST) { auto [new_child_lhs, new_child_rhs_opt, child_lhs_ranks, child_rhs_ranks] = transform_lists_of_structs(child_lhs, child_rhs, stream); - if (child_lhs_ranks) { + if (child_lhs_ranks || child_rhs_ranks) { auto transformed_lhs = make_transformed_input(lhs, new_child_lhs); - auto transformed_rhs = make_transformed_input(rhs.value(), new_child_rhs_opt.value()); + auto transformed_rhs = rhs ? std::optional{make_transformed_input( + rhs.value(), new_child_rhs_opt.value())} + : std::nullopt; return { transformed_lhs, transformed_rhs, std::move(child_lhs_ranks), std::move(child_rhs_ranks)}; } From 66cd1bc53055da2a37ddaa97aab9056a66999a28 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 15 Mar 2023 15:00:36 -0700 Subject: [PATCH 050/140] Minor cleanup Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 67bfabd8d98..b57272de6f4 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -392,10 +392,7 @@ std:: if (child_lhs.type().id() == type_id::STRUCT) { if (rhs) { auto child_lhs_rhs = cudf::detail::concatenate( - /*std::vector*/ std::vector{child_lhs, child_rhs.value()}, - stream, - default_mr); - + std::vector{child_lhs, child_rhs.value()}, stream, default_mr); // Dense ranks should be used because we are ranking two separate columns concatenating // together. auto const ranks = cudf::detail::rank(child_lhs_rhs->view(), @@ -419,9 +416,9 @@ std:: } else { // Dense ranks can accurately reflect the order of structs: structs compared equal will have // the same rank values. - // However, first ranks are computed faster and are good enough for ordering them. Structs - // compared equal always have consecutive rank values (in stable order) thus they are still - // sorted correctly by their ranks. + // However, first ranks are computed faster and are good enough for ordering them within one + // column. Structs compared equal always have consecutive rank values (in stable order) thus + // they are still sorted correctly by their ranks. auto child_lhs_ranks = cudf::detail::rank(child_lhs, rank_method::FIRST, order::ASCENDING, From e96427f76121f411538eb74d448c264744b1e44d Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 18 Mar 2023 12:58:16 -0700 Subject: [PATCH 051/140] Add test --- cpp/src/table/row_operators.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index be75e7a33cc..e80157f574e 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -385,7 +385,7 @@ std::pair> transform_lists_of_structs( // compared equal always have consecutive rank values (in stable order) thus they are still // sorted correctly by their ranks. auto ranks = cudf::detail::rank(child, - rank_method::FIRST, + rank_method::DENSE, order::ASCENDING, null_policy::EXCLUDE, null_order::BEFORE, From 11f856f9eac849bfd0785e8ac9d40f0b1673f8c7 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 18 Mar 2023 13:05:17 -0700 Subject: [PATCH 052/140] Fix bug in using rank method --- cpp/src/table/row_operators.cu | 10 ++++----- cpp/tests/sort/sort_nested_types_tests.cpp | 26 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index e80157f574e..ae55929f50d 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -379,11 +379,11 @@ std::pair> transform_lists_of_structs( auto const child = input.child(lists_column_view::child_column_index); if (child.type().id() == type_id::STRUCT) { - // Dense ranks can accurately reflect the order of structs: structs compared equal will have - // the same rank values. - // However, first ranks are computed faster and are good enough for ordering them. Structs - // compared equal always have consecutive rank values (in stable order) thus they are still - // sorted correctly by their ranks. + // Dense ranks should be used instead of first rank. + // Consider this example: input = [ [{0, "a"}, {3, "c"}], [{0, "a"}, {2, "b"}] ]. + // If first rank is used, transformed_input = [ [0, 3], [1, 2] ]. Comparing them will lead to + // the result row(0) < row(1) which is incorrect. + // With dense rank, ranks(input) = [ [0, 2], [0, 1] ], producing correct comparison. auto ranks = cudf::detail::rank(child, rank_method::DENSE, order::ASCENDING, diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index 81b859cb92d..2fb07a78059 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -24,6 +24,7 @@ using int32s_lists = cudf::test::lists_column_wrapper; using int32s_col = cudf::test::fixed_width_column_wrapper; +using strings_col = cudf::test::strings_column_wrapper; using structs_col = cudf::test::structs_column_wrapper; using namespace cudf::test::iterators; @@ -199,6 +200,31 @@ TEST_F(NestedListTest, SimpleListsOfStructsNoNulls) } } +TEST_F(NestedListTest, ListsOfEqualStructsNoNulls) +{ + auto const input = [] { + auto const get_structs = [] { + auto child0 = int32s_col{0, 3, 0, 1}; + auto child1 = strings_col{"a", "c", "a", "b"}; + return structs_col{{child0, child1}}; + }; + return cudf::make_lists_column( + 2, int32s_col{0, 2, 4}.release(), get_structs().release(), 0, {}); + }(); + + { + auto const expected_order = int32s_col{1, 0}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{0, 1}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + TEST_F(NestedListTest, SimpleListsOfStructsWithNulls) { auto const input = [] { From c6d18568d06e680dc115ef946587765a9954863c Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 18 Mar 2023 13:18:27 -0700 Subject: [PATCH 053/140] Rename variables and rewrite docs --- .../cudf/table/experimental/row_operators.cuh | 20 +++++++++--------- cpp/src/table/row_operators.cu | 21 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 394d5686fe6..bfe3fb45423 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -727,11 +727,11 @@ struct preprocessed_table { * columns in the table (unlike the `dremel_data` parameter, which is only as long as the number * of list columns). * @param flattened_input_aux_data The data structure generated from - * `cudf::structs::detail::flatten_nested_columns` that contains additional information used for - * row comparisons. - * @param transformed_structs_columns The intermediate columns resulted from transforming child - * of lists-of-structs columns into lists-of-integers columns and will be used for row - * comparison. + * `cudf::structs::detail::flatten_nested_columns` containing the input flattened table along + * with its corresponding flattened column orders and null orders. + * @param structs_ranked_columns Store the intermediate results from transforming the + * child columns of lists-of-structs columns into integer columns using `cudf::rank()` + * and will be used for row comparison. */ preprocessed_table( table_device_view_owner&& table, @@ -741,7 +741,7 @@ struct preprocessed_table { std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns); + std::vector>&& structs_ranked_columns); preprocessed_table( table_device_view_owner&& table, @@ -749,7 +749,7 @@ struct preprocessed_table { rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns); + std::vector>&& structs_ranked_columns); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -819,9 +819,9 @@ struct preprocessed_table { // that needs to be kept alive. std::unique_ptr _flattened_input_aux_data; - // Auxiliary data generated from transforming lists-of-structs into lists-of-integer - // that needs to be kept alive. - std::vector> _transformed_structs_aux_data; + // Intermediate columns generated from transforming the child columns of lists-of-structs columns + // into integer columns using `cudf::rank()`, also need to be kept alive. + std::vector> _structs_ranked_columns; }; /** diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index ae55929f50d..1920cddee84 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -359,7 +359,7 @@ namespace { * @param input The input column to transform * @param stream CUDA stream used for device memory operations and kernel launches * @return A pair of new column_view representing the transformed input and the generated rank - * (integer) column which needs to be kept alive + * (size_type) column which needs to be kept alive */ std::pair> transform_lists_of_structs( column_view const& input, rmm::cuda_stream_view stream) @@ -383,7 +383,7 @@ std::pair> transform_lists_of_structs( // Consider this example: input = [ [{0, "a"}, {3, "c"}], [{0, "a"}, {2, "b"}] ]. // If first rank is used, transformed_input = [ [0, 3], [1, 2] ]. Comparing them will lead to // the result row(0) < row(1) which is incorrect. - // With dense rank, ranks(input) = [ [0, 2], [0, 1] ], producing correct comparison. + // With dense rank, transformed_input = [ [0, 2], [0, 1] ], producing correct comparison. auto ranks = cudf::detail::rank(child, rank_method::DENSE, order::ASCENDING, @@ -402,7 +402,8 @@ std::pair> transform_lists_of_structs( } } } else if (input.type().id() == type_id::STRUCT) { - CUDF_UNREACHABLE("Structs columns should be flattened before calling this function."); + CUDF_FAIL( + "Structs columns containing lists should be flattened before reaching this function."); } return {input, nullptr}; @@ -493,7 +494,7 @@ std::shared_ptr preprocessed_table::create( flatten_nested_structs_of_lists(t, column_order, null_precedence, stream); // Next, transform any (nested) lists-of-structs column into lists-of-integers column. - auto [transformed_t, transformed_aux_data] = transform_lists_of_structs(flattened_t, stream); + auto [transformed_t, structs_ranked_columns] = transform_lists_of_structs(flattened_t, stream); check_lex_compatibility(transformed_t); @@ -518,7 +519,7 @@ std::shared_ptr preprocessed_table::create( std::move(dremel_data), std::move(d_dremel_device_view), std::move(flattened_t_aux_data), - std::move(transformed_aux_data))); + std::move(structs_ranked_columns))); } else { return std::shared_ptr( new preprocessed_table(std::move(d_t), @@ -526,7 +527,7 @@ std::shared_ptr preprocessed_table::create( std::move(d_null_precedence), std::move(d_depths), std::move(flattened_t_aux_data), - std::move(transformed_aux_data))); + std::move(structs_ranked_columns))); } } @@ -538,7 +539,7 @@ preprocessed_table::preprocessed_table( std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns) + std::vector>&& structs_ranked_columns) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -546,7 +547,7 @@ preprocessed_table::preprocessed_table( _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), _flattened_input_aux_data(std::move(flattened_input_aux_data)), - _transformed_structs_aux_data(std::move(transformed_structs_columns)) + _structs_ranked_columns(std::move(structs_ranked_columns)) { } @@ -556,7 +557,7 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& transformed_structs_columns) + std::vector>&& structs_ranked_columns) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -564,7 +565,7 @@ preprocessed_table::preprocessed_table( _dremel_data{}, _dremel_device_views{}, _flattened_input_aux_data(std::move(flattened_input_aux_data)), - _transformed_structs_aux_data(std::move(transformed_structs_columns)) + _structs_ranked_columns(std::move(structs_ranked_columns)) { } From 3003d7eba1fbb70584282baebdeb0667740973cb Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 18 Mar 2023 13:30:25 -0700 Subject: [PATCH 054/140] Only allow to use `sorting_physical_element_comparator` if there is lists-of-structs --- cpp/include/cudf/table/experimental/row_operators.cuh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index bfe3fb45423..abbd2f3aee6 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -902,6 +902,11 @@ class self_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const noexcept { + if constexpr (!std::is_same_v) { + CUDF_EXPECTS( + d_t->_structs_ranked_columns.size() == 0, + "The input table was preprocessed using a different type of physical element comparator."); + } return less_comparator{ device_row_comparator{ nullate, @@ -922,6 +927,12 @@ class self_comparator { auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const noexcept { + if constexpr (!std::is_same_v) { + CUDF_EXPECTS( + d_t->_structs_ranked_columns.size() == 0, + "The input table was preprocessed using a different type of physical element comparator."); + } + return less_equivalent_comparator{ device_row_comparator{ nullate, From 7b21434cbbd29e6c33b6976f88b472c92126e6a2 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 18 Mar 2023 13:49:05 -0700 Subject: [PATCH 055/140] Fix condition --- cpp/src/table/row_operators.cu | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 1920cddee84..57397d2db94 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -402,7 +402,10 @@ std::pair> transform_lists_of_structs( } } } else if (input.type().id() == type_id::STRUCT) { - CUDF_FAIL( + CUDF_EXPECTS( + std::all_of(input.child_begin(), + input.child_end(), + [](auto const& child) { return child.type().id() != type_id::LIST; }), "Structs columns containing lists should be flattened before reaching this function."); } From 66e278a5e79bd3e2be00039b1760905a38746103 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 18 Mar 2023 19:09:20 -0700 Subject: [PATCH 056/140] Remove `noexcept` --- cpp/include/cudf/table/experimental/row_operators.cuh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index abbd2f3aee6..de0d0d3581d 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -900,7 +900,7 @@ class self_comparator { template - auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const noexcept + auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { CUDF_EXPECTS( @@ -924,8 +924,7 @@ class self_comparator { template - auto less_equivalent(Nullate nullate = {}, - PhysicalElementComparator comparator = {}) const noexcept + auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { CUDF_EXPECTS( From 032f271c6aee8080a23794599f8e3a1adac66203 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 18 Mar 2023 19:24:59 -0700 Subject: [PATCH 057/140] Misc --- cpp/include/cudf/table/experimental/row_operators.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index de0d0d3581d..1341f36976d 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -907,6 +907,7 @@ class self_comparator { d_t->_structs_ranked_columns.size() == 0, "The input table was preprocessed using a different type of physical element comparator."); } + return less_comparator{ device_row_comparator{ nullate, From 2fa2b696a1c12415f01b87d60ebfd82e1d90eace Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 20 Mar 2023 14:56:59 -0700 Subject: [PATCH 058/140] Update docs Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index a801edea719..724dc05ba15 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -350,19 +350,19 @@ namespace lexicographic { namespace { /** - * @brief Transform a lists-of-structs column into lists-of-integers column. + * @brief Transform any nested lists-of-structs column into lists-of-integers column. * - * For lists-of-structs column at any nested level, the child structs column will be replaced by an - * integer column of its ranks generated using `cudf::rank()`. + * For a lists-of-structs column at any nested level, its child structs column will be replaced by a + * size_type column generated using `cudf::rank()`. * * If the input column is not lists-of-structs, or does not contain lists-of-structs at any nested - * level, the input will be passed through. + * level, the input will be passed through without any changes. * - * @param input The input column to transform - * @param + * @param lhs The input lhs column to transform + * @param rhs The input rhs column to transform (if available) * @param stream CUDA stream used for device memory operations and kernel launches * @return A pair of new column_view representing the transformed input and the generated rank - * (size_type) column which needs to be kept alive + * (size_type) column(s) which need to be kept alive */ std:: tuple, std::unique_ptr, std::unique_ptr> @@ -441,10 +441,10 @@ std:: } } } else if (lhs.type().id() == type_id::STRUCT) { - std::all_of(input.child_begin(), - input.child_end(), - [](auto const& child) { - return child.type().id() != type_id::LIST; }), + CUDF_EXPECTS( + std::all_of(lhs.child_begin(), + lhs.child_end(), + [](auto const& child) { return child.type().id() != type_id::LIST; }), "Structs columns containing lists should be flattened before reaching this function."); } @@ -452,16 +452,16 @@ std:: } /** - * @brief Transform any lists-of-structs column in a given table into lists-of-integers column. + * @brief Transform any lists-of-structs column in the given table(s) into lists-of-integers column. * - * If the rhs table is specified, its shape should be pre-checked to match with lhs through - * `check_shape_compatibility`. + * If the rhs table is specified, its shape should be pre-checked to match with lhs table using + * `check_shape_compatibility` before being passed into this function. * - * @param input The input table to transform - * @param tba + * @param lhs The input lhs table to transform + * @param rhs The input rhs table to transform (if available) * @param stream CUDA stream used for device memory operations and kernel launches * @return A pair of new table_view representing the transformed input and the generated rank - * (integer) column which needs to be kept alive + * (size_type) column(s) which need to be kept alive */ std::tuple, From 6a69fa78272c59c1ecc302f9f14347a43e0a1b08 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 20 Mar 2023 15:04:25 -0700 Subject: [PATCH 059/140] Rename variables Signed-off-by: Nghia Truong --- cpp/src/table/row_operators.cu | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 724dc05ba15..d83ff0d9518 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -473,25 +473,22 @@ transform_lists_of_structs(table_view const& lhs, { std::vector transformed_lhs_cols; std::vector transformed_rhs_cols; - std::vector> lhs_aux_cols; - std::vector> rhs_aux_cols; + std::vector> ranks_cols_lhs; + std::vector> ranks_cols_rhs; for (size_type child_idx = 0; child_idx < lhs.num_columns(); ++child_idx) { - auto const& col = lhs.column(child_idx); - auto [transformed_lhs, transformed_rhs_opt, lhs_aux_data, rhs_aux_data] = - transform_lists_of_structs( - col, - rhs ? std::optional{rhs.value().column(child_idx)} : std::nullopt, - stream); + auto const& col = lhs.column(child_idx); + auto [transformed_lhs, transformed_rhs_opt, ranks_lhs, ranks_rhs] = transform_lists_of_structs( + col, rhs ? std::optional{rhs.value().column(child_idx)} : std::nullopt, stream); transformed_lhs_cols.push_back(transformed_lhs); if (rhs) { transformed_rhs_cols.push_back(transformed_rhs_opt.value()); } - if (lhs_aux_data) { lhs_aux_cols.emplace_back(std::move(lhs_aux_data)); } - if (rhs_aux_data) { rhs_aux_cols.emplace_back(std::move(rhs_aux_data)); } + if (ranks_lhs) { ranks_cols_lhs.emplace_back(std::move(ranks_lhs)); } + if (ranks_rhs) { ranks_cols_rhs.emplace_back(std::move(ranks_rhs)); } } return {table_view{transformed_lhs_cols}, rhs ? std::optional{table_view{transformed_rhs_cols}} : std::nullopt, - std::move(lhs_aux_cols), - std::move(rhs_aux_cols)}; + std::move(ranks_cols_lhs), + std::move(ranks_cols_rhs)}; } /** From b291b9ed49e36cab59ce1cedf0ce12e03046028f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 20 Mar 2023 16:13:49 -0700 Subject: [PATCH 060/140] Add a helper function Signed-off-by: Nghia Truong --- cpp/tests/search/search_list_test.cpp | 49 +++++++++++++++++---------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index 1e97933fa4d..2d22eda7df0 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -349,10 +349,20 @@ TYPED_TEST(TypedListContainsTestColumnNeedles, ListsOfStructs) CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, *result, verbosity); } -struct ListLowerBound : public cudf::test::BaseFixture { +auto search_bounds(cudf::table_view const& t, + cudf::table_view const& values, + std::vector const& column_order, + std::vector const& null_precedence) +{ + auto result_lower_bound = cudf::lower_bound(t, values, column_order, null_precedence); + auto result_upper_bound = cudf::upper_bound(t, values, column_order, null_precedence); + return std::pair(std::move(result_lower_bound), std::move(result_upper_bound)); +} + +struct ListBinarySearch : public cudf::test::BaseFixture { }; -TEST_F(ListLowerBound, ListWithNulls) +TEST_F(ListBinarySearch, ListWithNulls) { { using lcw = cudf::test::lists_column_wrapper; @@ -363,29 +373,30 @@ TEST_F(ListLowerBound, ListWithNulls) }; auto const needles = lcw{ - lcw{{0, 4.22671e+32}, null_at(0)}, + lcw{{null, 4.22671e+32}, null_at(0)}, }; - auto const expect = int32s_col{0}; - auto const result = cudf::lower_bound(cudf::table_view{{haystack}}, - cudf::table_view{{needles}}, - {cudf::order::ASCENDING}, - {cudf::null_order::BEFORE}); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expect, *result); + auto const expect = int32s_col{0}; + auto const results = search_bounds(cudf::table_view{{haystack}}, + cudf::table_view{{needles}}, + {cudf::order::ASCENDING}, + {cudf::null_order::BEFORE}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expect, results.first->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expect, results.second->view()); } { using lcw = cudf::test::lists_column_wrapper; auto const col1 = lcw{ - lcw{{0}, null_at(0)}, // 0 - lcw{-80}, // 1 - lcw{-17}, // 2 + lcw{{null}, null_at(0)}, // 0 + lcw{-80}, // 1 + lcw{-17}, // 2 }; auto const col2 = lcw{ - lcw{27}, // 0 - lcw{{0}, null_at(0)}, // 1 - lcw{}, // 2 + lcw{27}, // 0 + lcw{{null}, null_at(0)}, // 1 + lcw{}, // 2 }; auto const val1 = lcw{ @@ -402,8 +413,10 @@ TEST_F(ListLowerBound, ListWithNulls) std::vector null_order_flags{cudf::null_order::BEFORE, cudf::null_order::BEFORE}; - auto const expect = int32s_col{3}; - auto const result = cudf::lower_bound(input, values, column_order, null_order_flags); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expect, *result); + auto const expected = int32s_col{3}; + auto const results = search_bounds( + cudf::table_view{{input}}, cudf::table_view{{values}}, column_order, null_order_flags); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, results.first->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, results.second->view()); } } From 50f29cdde0345ae83b1c8e409740f6a179b27ea2 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 20 Mar 2023 16:34:59 -0700 Subject: [PATCH 061/140] Add lists-of-structs test Signed-off-by: Nghia Truong --- cpp/tests/search/search_list_test.cpp | 61 ++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index 2d22eda7df0..9749413be15 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -33,7 +33,7 @@ using int32s_col = cudf::test::fixed_width_column_wrapper; using structs_col = cudf::test::structs_column_wrapper; using strings_col = cudf::test::strings_column_wrapper; -constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::FIRST_ERROR}; +constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::ALL_ERRORS}; constexpr int32_t null{0}; // Mark for null child elements at the current level constexpr int32_t XXX{0}; // Mark for null elements at all levels constexpr int32_t dont_care{0}; // Mark for elements that will be sliced off @@ -352,7 +352,8 @@ TYPED_TEST(TypedListContainsTestColumnNeedles, ListsOfStructs) auto search_bounds(cudf::table_view const& t, cudf::table_view const& values, std::vector const& column_order, - std::vector const& null_precedence) + std::vector const& null_precedence = { + cudf::null_order::BEFORE}) { auto result_lower_bound = cudf::lower_bound(t, values, column_order, null_precedence); auto result_upper_bound = cudf::upper_bound(t, values, column_order, null_precedence); @@ -420,3 +421,59 @@ TEST_F(ListBinarySearch, ListWithNulls) CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, results.second->view()); } } + +TEST_F(ListBinarySearch, ListsOfStructs) +{ + using tdata_col = cudf::test::fixed_width_column_wrapper; + + auto const haystack = [] { + auto offsets = int32s_col{0, 2, 3, 5, 8, 10}; + // clang-format off + auto data1 = tdata_col{1, 2, // + 3, // + 4, 5, // + 1, 3, 4, // + 0, 1 // + }; + auto data2 = tdata_col{1, 3, // + 2, // + 1, 1, // + 1, 2, 0, // + 1, 2 // + }; + // clang-format on + auto child = structs_col{{data1, data2}}; + return cudf::make_lists_column(5, offsets.release(), child.release(), 0, {}); + }(); + + auto const needles = [] { + auto offsets = int32s_col{0, 3, 4, 6, 9, 11, 13}; + // clang-format off + auto data1 = tdata_col{1, 2, 1, // + 1, // + 4, 1, // + 0, 1, // + 1, 0, // + 1, 3, 5, // + 0, 0 // + }; + auto data2 = tdata_col{1, 3, 0, // + 2, // + 1, 2, // + 1, 1, // + 1, 2, // + 0, 2, 2, // + 1, 3 // + }; + // clang-format on + auto child = structs_col{{data1, data2}}; + return cudf::make_lists_column(6, offsets.release(), child.release(), 0, {}); + }(); + + auto const results = search_bounds( + cudf::table_view{{*haystack}}, cudf::table_view{{*needles}}, {cudf::order::ASCENDING}); + auto const expected_lower_bound = int32s_col{1, 1, 2, 0, 2}; + auto const expected_upper_bound = int32s_col{1, 2, 0, 0, 2}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), verbosity); +} From 2dc2d15c51ff71e031251da5c389d840d719cffc Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 20 Mar 2023 19:45:41 -0700 Subject: [PATCH 062/140] Complete tests Signed-off-by: Nghia Truong --- cpp/tests/search/search_list_test.cpp | 73 ++++++++++++++++----------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index 9749413be15..342667450d2 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -426,54 +426,67 @@ TEST_F(ListBinarySearch, ListsOfStructs) { using tdata_col = cudf::test::fixed_width_column_wrapper; + // Haystack must be pre-sorted. auto const haystack = [] { - auto offsets = int32s_col{0, 2, 3, 5, 8, 10}; + auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 16, 18}; // clang-format off - auto data1 = tdata_col{1, 2, // - 3, // - 4, 5, // - 1, 3, 4, // - 0, 1 // + auto data1 = tdata_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 4, 6 }; - auto data2 = tdata_col{1, 3, // - 2, // - 1, 1, // - 1, 2, 0, // - 1, 2 // + auto data2 = tdata_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 5, 1 }; // clang-format on auto child = structs_col{{data1, data2}}; - return cudf::make_lists_column(5, offsets.release(), child.release(), 0, {}); + return cudf::make_lists_column(9, offsets.release(), child.release(), 0, {}); }(); auto const needles = [] { - auto offsets = int32s_col{0, 3, 4, 6, 9, 11, 13}; + auto offsets = int32s_col{0, 3, 4, 6, 8, 10, 13, 14, 15, 17}; // clang-format off - auto data1 = tdata_col{1, 2, 1, // - 1, // - 4, 1, // - 0, 1, // - 1, 0, // - 1, 3, 5, // - 0, 0 // + auto data1 = tdata_col{1, 2, 1, + 3, + 4, 1, + 0, 1, + 1, 0, + 1, 3, 5, + 3, + 3, + 0, 0 }; - auto data2 = tdata_col{1, 3, 0, // - 2, // - 1, 2, // - 1, 1, // - 1, 2, // - 0, 2, 2, // - 1, 3 // + auto data2 = tdata_col{1, 3, 0, + 3, + 1, 2, + 1, 1, + 1, 2, + 0, 2, 2, + 3, + 3, + 1, 3 }; // clang-format on auto child = structs_col{{data1, data2}}; - return cudf::make_lists_column(6, offsets.release(), child.release(), 0, {}); + return cudf::make_lists_column(9, offsets.release(), child.release(), 0, {}); }(); auto const results = search_bounds( cudf::table_view{{*haystack}}, cudf::table_view{{*needles}}, {cudf::order::ASCENDING}); - auto const expected_lower_bound = int32s_col{1, 1, 2, 0, 2}; - auto const expected_upper_bound = int32s_col{1, 2, 0, 0, 2}; + auto const expected_lower_bound = int32s_col{1, 1, 4, 0, 0, 0, 1, 1, 0}; + auto const expected_upper_bound = int32s_col{1, 4, 4, 0, 0, 0, 4, 4, 0}; CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), verbosity); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), verbosity); } From e8189223045cdb890845796b2baf5e5a90ac709f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 20 Mar 2023 19:57:42 -0700 Subject: [PATCH 063/140] Add a test with equal structs Signed-off-by: Nghia Truong --- cpp/tests/search/search_list_test.cpp | 82 +++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index 342667450d2..7933938df3a 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -456,7 +456,7 @@ TEST_F(ListBinarySearch, ListsOfStructs) }(); auto const needles = [] { - auto offsets = int32s_col{0, 3, 4, 6, 8, 10, 13, 14, 15, 17}; + auto offsets = int32s_col{0, 3, 4, 6, 8, 10, 13, 14, 15}; // clang-format off auto data1 = tdata_col{1, 2, 1, 3, @@ -465,8 +465,7 @@ TEST_F(ListBinarySearch, ListsOfStructs) 1, 0, 1, 3, 5, 3, - 3, - 0, 0 + 3 }; auto data2 = tdata_col{1, 3, 0, 3, @@ -475,18 +474,89 @@ TEST_F(ListBinarySearch, ListsOfStructs) 1, 2, 0, 2, 2, 3, + 3 + }; + // clang-format on + auto child = structs_col{{data1, data2}}; + return cudf::make_lists_column(8, offsets.release(), child.release(), 0, {}); + }(); + + auto const results = search_bounds( + cudf::table_view{{*haystack}}, cudf::table_view{{*needles}}, {cudf::order::ASCENDING}); + auto const expected_lower_bound = int32s_col{1, 1, 4, 0, 0, 0, 1, 1}; + auto const expected_upper_bound = int32s_col{1, 4, 4, 0, 0, 0, 4, 4}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), verbosity); +} + +TEST_F(ListBinarySearch, ListsOfEqualStructsInTwoTables) +{ + using tdata_col = cudf::test::fixed_width_column_wrapper; + + // Haystack must be pre-sorted. + auto const haystack = [] { + auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 16, 18}; + // clang-format off + auto data1 = tdata_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 4, 6 + }; + auto data2 = tdata_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 5, 1 + }; + // clang-format on + auto child = structs_col{{data1, data2}}; + return cudf::make_lists_column(9, offsets.release(), child.release(), 0, {}); + }(); + + auto const needles = [] { + auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 15, 17}; + // clang-format off + auto data1 = tdata_col{1, 2, + 3, + 4, + 5, + 4, 5, + 5, 5, 4, + 4, 5, 4, + 4, 4, + 4, 6 + }; + auto data2 = tdata_col{1, 2, 3, - 1, 3 + 4, + 5, + 4, 5, + 5, 5, 4, + 4, 5, 4, + 4, 4, + 5, 1 }; // clang-format on auto child = structs_col{{data1, data2}}; return cudf::make_lists_column(9, offsets.release(), child.release(), 0, {}); }(); + // In this search, the two table have many equal structs. + // This help to verify the internal implementation of two-table lex comparator in which the + // structs column of two input tables are concatenated, ranked, then split. auto const results = search_bounds( cudf::table_view{{*haystack}}, cudf::table_view{{*needles}}, {cudf::order::ASCENDING}); - auto const expected_lower_bound = int32s_col{1, 1, 4, 0, 0, 0, 1, 1, 0}; - auto const expected_upper_bound = int32s_col{1, 4, 4, 0, 0, 0, 4, 4, 0}; + auto const expected_lower_bound = int32s_col{0, 1, 4, 9, 4, 9, 5, 4, 8}; + auto const expected_upper_bound = int32s_col{1, 4, 4, 9, 5, 9, 8, 4, 9}; CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), verbosity); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), verbosity); } From e5dc067fbd1cc426ea9da52ee51704756d7b5112 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 20 Mar 2023 21:33:45 -0700 Subject: [PATCH 064/140] Remove type alias Signed-off-by: Nghia Truong --- cpp/tests/search/search_list_test.cpp | 144 +++++++++++++------------- 1 file changed, 70 insertions(+), 74 deletions(-) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index 7933938df3a..bdab6e5d4ec 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -424,31 +424,29 @@ TEST_F(ListBinarySearch, ListWithNulls) TEST_F(ListBinarySearch, ListsOfStructs) { - using tdata_col = cudf::test::fixed_width_column_wrapper; - // Haystack must be pre-sorted. auto const haystack = [] { auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 16, 18}; // clang-format off - auto data1 = tdata_col{1, 2, - 3, - 3, - 3, - 4, 5, - 4, 5, 4, - 4, 5, 4, - 4, 5, 4, - 4, 6 + auto data1 = int32s_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 4, 6 }; - auto data2 = tdata_col{1, 2, - 3, - 3, - 3, - 4, 5, - 4, 5, 4, - 4, 5, 4, - 4, 5, 4, - 5, 1 + auto data2 = int32s_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 5, 1 }; // clang-format on auto child = structs_col{{data1, data2}}; @@ -458,23 +456,23 @@ TEST_F(ListBinarySearch, ListsOfStructs) auto const needles = [] { auto offsets = int32s_col{0, 3, 4, 6, 8, 10, 13, 14, 15}; // clang-format off - auto data1 = tdata_col{1, 2, 1, - 3, - 4, 1, - 0, 1, - 1, 0, - 1, 3, 5, - 3, - 3 + auto data1 = int32s_col{1, 2, 1, + 3, + 4, 1, + 0, 1, + 1, 0, + 1, 3, 5, + 3, + 3 }; - auto data2 = tdata_col{1, 3, 0, - 3, - 1, 2, - 1, 1, - 1, 2, - 0, 2, 2, - 3, - 3 + auto data2 = int32s_col{1, 3, 0, + 3, + 1, 2, + 1, 1, + 1, 2, + 0, 2, 2, + 3, + 3 }; // clang-format on auto child = structs_col{{data1, data2}}; @@ -491,31 +489,29 @@ TEST_F(ListBinarySearch, ListsOfStructs) TEST_F(ListBinarySearch, ListsOfEqualStructsInTwoTables) { - using tdata_col = cudf::test::fixed_width_column_wrapper; - // Haystack must be pre-sorted. auto const haystack = [] { auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 16, 18}; // clang-format off - auto data1 = tdata_col{1, 2, - 3, - 3, - 3, - 4, 5, - 4, 5, 4, - 4, 5, 4, - 4, 5, 4, - 4, 6 + auto data1 = int32s_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 4, 6 }; - auto data2 = tdata_col{1, 2, - 3, - 3, - 3, - 4, 5, - 4, 5, 4, - 4, 5, 4, - 4, 5, 4, - 5, 1 + auto data2 = int32s_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 5, 1 }; // clang-format on auto child = structs_col{{data1, data2}}; @@ -525,25 +521,25 @@ TEST_F(ListBinarySearch, ListsOfEqualStructsInTwoTables) auto const needles = [] { auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 15, 17}; // clang-format off - auto data1 = tdata_col{1, 2, - 3, - 4, - 5, - 4, 5, - 5, 5, 4, - 4, 5, 4, - 4, 4, - 4, 6 + auto data1 = int32s_col{1, 2, + 3, + 4, + 5, + 4, 5, + 5, 5, 4, + 4, 5, 4, + 4, 4, + 4, 6 }; - auto data2 = tdata_col{1, 2, - 3, - 4, - 5, - 4, 5, - 5, 5, 4, - 4, 5, 4, - 4, 4, - 5, 1 + auto data2 = int32s_col{1, 2, + 3, + 4, + 5, + 4, 5, + 5, 5, 4, + 4, 5, 4, + 4, 4, + 5, 1 }; // clang-format on auto child = structs_col{{data1, data2}}; From 0818370236787722105d771f7ae23dd6ea529e96 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 14:19:21 -0700 Subject: [PATCH 065/140] Complete implementation --- .../cudf/table/experimental/row_operators.cuh | 29 ++++++++++++--- cpp/src/table/row_operators.cu | 37 ++++++++++++++++--- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 846bebfe10b..0e125ce34f6 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -330,8 +330,8 @@ class device_row_comparator { null_order null_precedence = null_order::BEFORE, int depth = 0, PhysicalElementComparator comparator = {}, - optional_dremel_view l_dremel_device_view = {}, - optional_dremel_view r_dremel_device_view = {}) + optional_dremel_view l_dremel_device_view = thrust::nullopt, + optional_dremel_view r_dremel_device_view = thrust::nullopt) : _lhs{lhs}, _rhs{rhs}, _check_nulls{check_nulls}, @@ -408,7 +408,14 @@ class device_row_comparator { return cudf::type_dispatcher( lcol.type(), - element_comparator{_check_nulls, lcol, rcol, _null_precedence, depth, _comparator}, + element_comparator{_check_nulls, + lcol, + rcol, + _null_precedence, + depth, + _comparator, + _l_dremel_device_view, + _r_dremel_device_view}, lhs_element_index, rhs_element_index); } @@ -561,9 +568,21 @@ class device_row_comparator { auto idx = list_column_index++; return std::make_tuple(optional_dremel_view(_l_dremel[idx]), optional_dremel_view(_r_dremel[idx])); - } else { - return std::make_tuple(optional_dremel_view{}, optional_dremel_view{}); + } else if (_lhs.column(i).type().id() == type_id::STRUCT && + _lhs.column(i).num_child_columns() > 0) { + // Any structs column has already been processed to have only 1 child. + auto child = _lhs.column(i).child(0); + while (child.type().id() == type_id::STRUCT && child.num_child_columns() > 0) { + child = child.child(0); + } + if (child.type().id() == type_id::LIST) { + auto idx = list_column_index++; + + return std::make_tuple(optional_dremel_view(_l_dremel[idx]), + optional_dremel_view(_r_dremel[idx])); + } } + return std::make_tuple(thrust::nullopt, thrust::nullopt); }(); auto element_comp = element_comparator{_check_nulls, _lhs.column(i), diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 6997de18be5..dc4716dfab0 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -180,8 +180,9 @@ auto decompose_structs(table_view table, c->children[lists_column_view::child_column_index].get(), branch, depth + 1); } else if (c->type().id() == type_id::STRUCT) { for (size_t child_idx = 0; child_idx < c->children.size(); ++child_idx) { - if (child_idx > 0) { - verticalized_col_depths.push_back(depth + 1); + if (child_idx > 0 || + (child_idx == 0 && c->children[0]->type().id() == type_id::LIST)) { + verticalized_col_depths.push_back(child_idx == 0 ? depth : depth + 1); branch = &flattened.emplace_back(); } recursive_child(c->children[child_idx].get(), branch, depth + 1); @@ -194,6 +195,18 @@ auto decompose_structs(table_view table, for (auto const& branch : flattened) { column_view temp_col = *branch.back(); + + if (temp_col.type().id() == type_id::STRUCT && + (temp_col.num_children() > 0 && temp_col.child(0).type().id() == type_id::LIST)) { + temp_col = column_view(temp_col.type(), + temp_col.size(), + temp_col.head(), + temp_col.null_mask(), + UNKNOWN_NULL_COUNT, + temp_col.offset(), + {}); + } + for (auto it = branch.crbegin() + 1; it < branch.crend(); ++it) { auto const& prev_col = *(*it); auto children = @@ -260,7 +273,7 @@ auto decompose_structs(table_view table, * This helper function generates dremel data for any list-type columns in a * table. This data is necessary for lexicographic comparisons. */ -auto list_lex_preprocess(table_view table, rmm::cuda_stream_view stream) +auto list_lex_preprocess(table_view const& table, rmm::cuda_stream_view stream) { std::vector dremel_data; std::vector dremel_device_views; @@ -268,6 +281,22 @@ auto list_lex_preprocess(table_view table, rmm::cuda_stream_view stream) if (col.type().id() == type_id::LIST) { dremel_data.push_back(detail::get_comparator_data(col, {}, false, stream)); dremel_device_views.push_back(dremel_data.back()); + } else if (col.type().id() == type_id::STRUCT) { + if (col.num_children() == 0) { continue; } + CUDF_EXPECTS(col.num_children() == 1, + "Structs column should be processed to have either 0 or 1 child"); + + auto child = col.child(0); + while (child.type().id() == type_id::STRUCT) { + if (child.num_children() == 0) { break; } + CUDF_EXPECTS(child.num_children() == 1, + "Structs column should be processed to have either 0 or 1 child"); + child = child.child(0); + } + if (child.type().id() == type_id::LIST) { + dremel_data.push_back(detail::get_comparator_data(child, {}, false, stream)); + dremel_device_views.push_back(dremel_data.back()); + } } } auto d_dremel_device_views = detail::make_device_uvector_sync( @@ -293,8 +322,6 @@ void check_lex_compatibility(table_view const& input) check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { - CUDF_EXPECTS(child->type().id() != type_id::LIST, - "Cannot lexicographic compare a table with a STRUCT of LIST column"); check_column(*child); } } From 1667b27aec8a9ae0fe12c145e4dc448658971baa Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 14:47:21 -0700 Subject: [PATCH 066/140] Fix existing test --- cpp/tests/groupby/structs_tests.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cpp/tests/groupby/structs_tests.cpp b/cpp/tests/groupby/structs_tests.cpp index a7bc6016307..57c93db50b5 100644 --- a/cpp/tests/groupby/structs_tests.cpp +++ b/cpp/tests/groupby/structs_tests.cpp @@ -294,11 +294,12 @@ TYPED_TEST(groupby_structs_test, all_null_input) test_sum_agg(keys, values, expected_keys, expected_values); } -TYPED_TEST(groupby_structs_test, lists_are_unsupported) +TYPED_TEST(groupby_structs_test, lists_as_keys) { using V = int32_t; // Type of Aggregation Column. using M0 = int32_t; // Type of STRUCT's first (i.e. 0th) member. using M1 = TypeParam; // Type of STRUCT's second (i.e. 1th) member. + using R = cudf::detail::target_type_t; // clang-format off auto values = fwcw { 0, 1, 2, 3, 4 }; @@ -307,5 +308,12 @@ TYPED_TEST(groupby_structs_test, lists_are_unsupported) // clang-format on auto keys = cudf::test::structs_column_wrapper{{member_0, member_1}}; - EXPECT_THROW(test_sum_agg(keys, values, keys, values), cudf::logic_error); + // clang-format off + auto expected_values = fwcw { 3, 5, 2 }; + auto expected_member_0 = lcw { {1,1}, {2,2}, {3,3} }; + auto expected_member_1 = fwcw{ 1, 2, 3 }; + // clang-format on + auto expected_keys = cudf::test::structs_column_wrapper{{expected_member_0, expected_member_1}}; + + test_sum_agg(keys, values, expected_keys, expected_values); } From e2cc0ab2c1b91a0f1e4dbe5230603f13195a3568 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 14:57:12 -0700 Subject: [PATCH 067/140] Fix test utilities --- cpp/tests/utilities/column_utilities.cu | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cpp/tests/utilities/column_utilities.cu b/cpp/tests/utilities/column_utilities.cu index 3a94aac1cc9..133ca99b31f 100644 --- a/cpp/tests/utilities/column_utilities.cu +++ b/cpp/tests/utilities/column_utilities.cu @@ -543,7 +543,7 @@ struct column_comparator_impl { auto const comparator = cudf::experimental::row::equality::two_table_comparator{ lhs_tview, rhs_tview, cudf::get_default_stream()}; - auto const has_nulls = cudf::has_nested_nulls(lhs_tview) or cudf::has_nested_nulls(rhs_tview); + auto const has_nulls = cudf::has_nulls(lhs_tview) or cudf::has_nulls(rhs_tview); auto const device_comparator = comparator.equal_to(cudf::nullate::DYNAMIC{has_nulls}); @@ -556,18 +556,22 @@ struct column_comparator_impl { lhs_row_indices.size(), cudf::get_default_stream()); // worst case: everything different auto input_iter = thrust::make_counting_iterator(0); + auto diff_map = rmm::device_uvector(lhs_row_indices.size(), cudf::get_default_stream()); + thrust::transform( rmm::exec_policy(cudf::get_default_stream()), input_iter, input_iter + lhs_row_indices.size(), - differences.begin(), + diff_map.begin(), ComparatorType( *d_lhs_row_indices, *d_rhs_row_indices, fp_ulps, device_comparator, *d_lhs, *d_rhs)); - auto diff_iter = thrust::remove(rmm::exec_policy(cudf::get_default_stream()), - differences.begin(), - differences.end(), - 0); // remove the zero entries + auto diff_iter = thrust::copy_if(rmm::exec_policy(cudf::get_default_stream()), + input_iter, + input_iter + lhs_row_indices.size(), + diff_map.begin(), + differences.begin(), + thrust::identity{}); differences.resize(thrust::distance(differences.begin(), diff_iter), cudf::get_default_stream()); // shrink back down From 64c6605655beb18f9af2b4c61de5b8f927586cf5 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 14:57:45 -0700 Subject: [PATCH 068/140] Add tests --- cpp/tests/CMakeLists.txt | 4 +- cpp/tests/sort/sort_nested_types_tests.cpp | 181 +++++++++++++++++++++ 2 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 cpp/tests/sort/sort_nested_types_tests.cpp diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index bd4077aff4e..6df646b8cae 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -295,8 +295,8 @@ endif() # ################################################################################################## # * sort tests ------------------------------------------------------------------------------------ ConfigureTest( - SORT_TEST sort/segmented_sort_tests.cpp sort/sort_test.cpp sort/stable_sort_tests.cpp - sort/rank_test.cpp + SORT_TEST sort/segmented_sort_tests.cpp sort/sort_nested_types_tests.cpp sort/sort_test.cpp + sort/stable_sort_tests.cpp sort/rank_test.cpp GPUS 1 PERCENT 70 ) diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp new file mode 100644 index 00000000000..f7b52940d30 --- /dev/null +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2023, 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 +#include +#include +#include + +#include +#include + +using int32s_lists = cudf::test::lists_column_wrapper; +using int32s_col = cudf::test::fixed_width_column_wrapper; +using strings_col = cudf::test::strings_column_wrapper; +using structs_col = cudf::test::structs_column_wrapper; + +using namespace cudf::test::iterators; + +constexpr auto null{0}; + +struct NestedStructTest : public cudf::test::BaseFixture { +}; + +TEST_F(NestedStructTest, SimpleStructsOfListsNoNulls) +{ + auto const input = [] { + auto child = int32s_lists{{4, 2, 0}, {2}, {0, 5}, {1, 5}, {4, 1}}; + return structs_col{{child}}; + }(); + + { + auto const expected_order = int32s_col{2, 3, 1, 4, 0}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{0, 4, 1, 3, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, SimpleStructsOfListsWithNulls) +{ + auto const input = [] { + auto child = + int32s_lists{{{4, 2, null}, null_at(2)}, {2}, {{null, 5}, null_at(0)}, {0, 5}, {4, 1}}; + return structs_col{{child}}; + }(); + + { + auto const expected_order = int32s_col{2, 3, 1, 4, 0}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{0, 4, 1, 3, 2}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsHaveListsNoNulls) +{ + // Input has equal elements, thus needs to tested by stable sort. + auto const input = [] { + auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1}}; + }(); + + { + auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; + auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{2, 0, 3, 4, 1, 5, 6}; + auto const order = + cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsHaveListsWithNulls) +{ + // Input has equal elements, thus needs to tested by stable sort. + auto const input = [] { + auto child0 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1}, null_at(2)}; + }(); + + { + auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; + auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{3, 0, 4, 5, 1, 6, 7, 2}; + auto const order = + cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsOfStructsHaveListsNoNulls) +{ + // Input has equal elements, thus needs to tested by stable sort. + auto const input = [] { + auto child0 = [] { + auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1}}; + }(); + auto child1 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child2 = int32s_col{1, 2, 5, 0, 3, 3, 4}; + return structs_col{{child0, child1, child2}}; + }(); + + { + auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; + auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; + auto const order = + cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, StructsOfStructsHaveListsWithNulls) +{ + // Input has equal elements, thus needs to tested by stable sort. + auto const input = [] { + auto child0 = [] { + auto child0 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child1 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1}, null_at(2)}; + }(); + auto child1 = + int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; + auto child2 = int32s_col{{1, 2, null, 5, null, 3, 3, 4}, nulls_at({2, 4})}; + return structs_col{{child0, child1, child2}, null_at(2)}; + }(); + + { + auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; + auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; + auto const order = + cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} From f75f9c5e147445d5e68810d243a6746775ea0737 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 18:29:28 -0700 Subject: [PATCH 069/140] Simplify code --- cpp/src/table/row_operators.cu | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index dc4716dfab0..b8fe3b17062 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -282,19 +282,15 @@ auto list_lex_preprocess(table_view const& table, rmm::cuda_stream_view stream) dremel_data.push_back(detail::get_comparator_data(col, {}, false, stream)); dremel_device_views.push_back(dremel_data.back()); } else if (col.type().id() == type_id::STRUCT) { - if (col.num_children() == 0) { continue; } - CUDF_EXPECTS(col.num_children() == 1, - "Structs column should be processed to have either 0 or 1 child"); - - auto child = col.child(0); - while (child.type().id() == type_id::STRUCT) { - if (child.num_children() == 0) { break; } - CUDF_EXPECTS(child.num_children() == 1, + auto col_iter = col; + while (col_iter.type().id() == type_id::STRUCT) { + if (col_iter.num_children() == 0) { break; } + CUDF_EXPECTS(col_iter.num_children() == 1, "Structs column should be processed to have either 0 or 1 child"); - child = child.child(0); + col_iter = col_iter.child(0); } - if (child.type().id() == type_id::LIST) { - dremel_data.push_back(detail::get_comparator_data(child, {}, false, stream)); + if (col_iter.type().id() == type_id::LIST) { + dremel_data.push_back(detail::get_comparator_data(col_iter, {}, false, stream)); dremel_device_views.push_back(dremel_data.back()); } } From 0c8a62a84a9d640b485864bd38cdca168595a4bc Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 18:33:01 -0700 Subject: [PATCH 070/140] Remove redundant code --- cpp/src/table/row_operators.cu | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index b8fe3b17062..39fe5fcfc14 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -281,18 +281,6 @@ auto list_lex_preprocess(table_view const& table, rmm::cuda_stream_view stream) if (col.type().id() == type_id::LIST) { dremel_data.push_back(detail::get_comparator_data(col, {}, false, stream)); dremel_device_views.push_back(dremel_data.back()); - } else if (col.type().id() == type_id::STRUCT) { - auto col_iter = col; - while (col_iter.type().id() == type_id::STRUCT) { - if (col_iter.num_children() == 0) { break; } - CUDF_EXPECTS(col_iter.num_children() == 1, - "Structs column should be processed to have either 0 or 1 child"); - col_iter = col_iter.child(0); - } - if (col_iter.type().id() == type_id::LIST) { - dremel_data.push_back(detail::get_comparator_data(col_iter, {}, false, stream)); - dremel_device_views.push_back(dremel_data.back()); - } } } auto d_dremel_device_views = detail::make_device_uvector_sync( From 01acdafc94f0fdc13ab1ca21656ff34a325fe565 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 18:44:26 -0700 Subject: [PATCH 071/140] Revert all changes in `row_operators.cuh` --- .../cudf/table/experimental/row_operators.cuh | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 0e125ce34f6..846bebfe10b 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -330,8 +330,8 @@ class device_row_comparator { null_order null_precedence = null_order::BEFORE, int depth = 0, PhysicalElementComparator comparator = {}, - optional_dremel_view l_dremel_device_view = thrust::nullopt, - optional_dremel_view r_dremel_device_view = thrust::nullopt) + optional_dremel_view l_dremel_device_view = {}, + optional_dremel_view r_dremel_device_view = {}) : _lhs{lhs}, _rhs{rhs}, _check_nulls{check_nulls}, @@ -408,14 +408,7 @@ class device_row_comparator { return cudf::type_dispatcher( lcol.type(), - element_comparator{_check_nulls, - lcol, - rcol, - _null_precedence, - depth, - _comparator, - _l_dremel_device_view, - _r_dremel_device_view}, + element_comparator{_check_nulls, lcol, rcol, _null_precedence, depth, _comparator}, lhs_element_index, rhs_element_index); } @@ -568,21 +561,9 @@ class device_row_comparator { auto idx = list_column_index++; return std::make_tuple(optional_dremel_view(_l_dremel[idx]), optional_dremel_view(_r_dremel[idx])); - } else if (_lhs.column(i).type().id() == type_id::STRUCT && - _lhs.column(i).num_child_columns() > 0) { - // Any structs column has already been processed to have only 1 child. - auto child = _lhs.column(i).child(0); - while (child.type().id() == type_id::STRUCT && child.num_child_columns() > 0) { - child = child.child(0); - } - if (child.type().id() == type_id::LIST) { - auto idx = list_column_index++; - - return std::make_tuple(optional_dremel_view(_l_dremel[idx]), - optional_dremel_view(_r_dremel[idx])); - } + } else { + return std::make_tuple(optional_dremel_view{}, optional_dremel_view{}); } - return std::make_tuple(thrust::nullopt, thrust::nullopt); }(); auto element_comp = element_comparator{_check_nulls, _lhs.column(i), From 93942560a049872c1700beae8178bf561a755f65 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 18:50:20 -0700 Subject: [PATCH 072/140] Fix typo --- cpp/tests/sort/sort_nested_types_tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index f7b52940d30..8702887f5e3 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -77,7 +77,7 @@ TEST_F(NestedStructTest, SimpleStructsOfListsWithNulls) TEST_F(NestedStructTest, StructsHaveListsNoNulls) { - // Input has equal elements, thus needs to tested by stable sort. + // Input has equal elements, thus needs to be tested by stable sort. auto const input = [] { auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; auto child1 = int32s_col{1, 2, 5, 0, 3, 3, 4}; @@ -100,7 +100,7 @@ TEST_F(NestedStructTest, StructsHaveListsNoNulls) TEST_F(NestedStructTest, StructsHaveListsWithNulls) { - // Input has equal elements, thus needs to tested by stable sort. + // Input has equal elements, thus needs to be tested by stable sort. auto const input = [] { auto child0 = int32s_lists{{{4, 2, null}, null_at(2)}, {}, {} /*NULL*/, {5}, {4, 1}, {4, 0}, {}, {}}; @@ -124,7 +124,7 @@ TEST_F(NestedStructTest, StructsHaveListsWithNulls) TEST_F(NestedStructTest, StructsOfStructsHaveListsNoNulls) { - // Input has equal elements, thus needs to tested by stable sort. + // Input has equal elements, thus needs to be tested by stable sort. auto const input = [] { auto child0 = [] { auto child0 = int32s_lists{{4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}}; @@ -152,7 +152,7 @@ TEST_F(NestedStructTest, StructsOfStructsHaveListsNoNulls) TEST_F(NestedStructTest, StructsOfStructsHaveListsWithNulls) { - // Input has equal elements, thus needs to tested by stable sort. + // Input has equal elements, thus needs to be tested by stable sort. auto const input = [] { auto child0 = [] { auto child0 = From 053722545c72475d95e8d2178207f914544978a1 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 19:31:05 -0700 Subject: [PATCH 073/140] Update docs --- cpp/src/table/row_operators.cu | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 39fe5fcfc14..ecce9652589 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -85,13 +85,17 @@ table_view remove_struct_child_offsets(table_view table) /** * @brief Decompose all struct columns in a table * - * If a struct column is a tree with N leaves, then this function decomposes the tree into + * If a structs column is a tree with N leaves, then this function decomposes the tree into * N "linear trees" (branch factor == 1) and prunes common parents. Also returns a vector of * per-column `depth`s. * * A `depth` value is the number of nested levels as parent of the column in the original, * non-decomposed table, which are pruned during decomposition. * + * Special handling is needed in the cases of structs column having lists as its first child. In + * such situations, the function decomposes the tree of N leaves into N+1 linear trees in which the + * second tree is also leaf of the first tree extracted out. + * * For example, if the original table has a column `Struct, decimal>`, * * S1 @@ -113,7 +117,7 @@ table_view remove_struct_child_offsets(table_view table) * The depth of the first column is 0 because it contains all its parent levels, while the depth * of the second column is 2 because two of its parent struct levels were pruned. * - * Similarly, a struct column of type Struct> is decomposed as follows + * Similarly, a struct column of type `Struct>` is decomposed as follows * * S1 * / \ @@ -148,6 +152,10 @@ table_view remove_struct_child_offsets(table_view table) * The list parents are still needed to define the range of elements in the leaf that belong to the * same row. * + * In the case of structs column having its first child is a lists column such as + * `Struct, int>`, after decomposition we get three columns `Struct>`, + * `List`, and `int`. + * * @param table The table whose struct columns to decompose. * @param column_order The per-column order if using output with lexicographic comparison * @param null_precedence The per-column null precedence From 6ff8edecc95547744cde48dc2f0c64323bc05845 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 20:44:09 -0700 Subject: [PATCH 074/140] Add comments --- cpp/src/table/row_operators.cu | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index ecce9652589..75c0e0509a7 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -94,7 +94,8 @@ table_view remove_struct_child_offsets(table_view table) * * Special handling is needed in the cases of structs column having lists as its first child. In * such situations, the function decomposes the tree of N leaves into N+1 linear trees in which the - * second tree is also leaf of the first tree extracted out. + * second tree is also leaf of the first tree extracted out. This is to make sure there is no + * structs column having child lists column in the output. * * For example, if the original table has a column `Struct, decimal>`, * @@ -188,6 +189,13 @@ auto decompose_structs(table_view table, c->children[lists_column_view::child_column_index].get(), branch, depth + 1); } else if (c->type().id() == type_id::STRUCT) { for (size_t child_idx = 0; child_idx < c->children.size(); ++child_idx) { + // When child_idx == 0, we also cut off the current branch if its first child is a + // lists column. + // In such cases, the last column of the current branch will be `Struct` and + // it will be modified to empty struct type `Struct<>` later on. + // In addition, the new cut out branch will be set to have the same depth as the + // current branch instead of higher depth. By this way, we can avoid the new branch + // to be ignored in `device_row_comparator` when the current branch has zero child. if (child_idx > 0 || (child_idx == 0 && c->children[0]->type().id() == type_id::LIST)) { verticalized_col_depths.push_back(child_idx == 0 ? depth : depth + 1); @@ -204,6 +212,7 @@ auto decompose_structs(table_view table, for (auto const& branch : flattened) { column_view temp_col = *branch.back(); + // Change `Struct` into empty struct type `Struct<>`. if (temp_col.type().id() == type_id::STRUCT && (temp_col.num_children() > 0 && temp_col.child(0).type().id() == type_id::LIST)) { temp_col = column_view(temp_col.type(), From b806d14ab5c766e10f06082c196941a810c08833 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 20:45:38 -0700 Subject: [PATCH 075/140] Change docs --- cpp/src/table/row_operators.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 75c0e0509a7..2a49a47a9e3 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -94,8 +94,8 @@ table_view remove_struct_child_offsets(table_view table) * * Special handling is needed in the cases of structs column having lists as its first child. In * such situations, the function decomposes the tree of N leaves into N+1 linear trees in which the - * second tree is also leaf of the first tree extracted out. This is to make sure there is no - * structs column having child lists column in the output. + * second tree was generated by extracting out leaf of the first tree. This is to make sure there is + * no structs column having child lists column in the output. * * For example, if the original table has a column `Struct, decimal>`, * From 7c63726f9d17d6da846358b69025982758254272 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 20:58:06 -0700 Subject: [PATCH 076/140] Fix style --- cpp/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 6df646b8cae..04cf3f838b5 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -296,7 +296,7 @@ endif() # * sort tests ------------------------------------------------------------------------------------ ConfigureTest( SORT_TEST sort/segmented_sort_tests.cpp sort/sort_nested_types_tests.cpp sort/sort_test.cpp - sort/stable_sort_tests.cpp sort/rank_test.cpp + sort/stable_sort_tests.cpp sort/rank_test.cpp GPUS 1 PERCENT 70 ) From 422f93c0ac42290e586ceaed17fab2fcef2c4983 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 23 Mar 2023 21:32:32 -0700 Subject: [PATCH 077/140] Fix docs --- cpp/src/table/row_operators.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 2a49a47a9e3..30f8f89fd21 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -154,8 +154,8 @@ table_view remove_struct_child_offsets(table_view table) * same row. * * In the case of structs column having its first child is a lists column such as - * `Struct, int>`, after decomposition we get three columns `Struct>`, - * `List`, and `int`. + * `Struct, float>`, after decomposition we get three columns `Struct<>`, + * `List`, and `float`. * * @param table The table whose struct columns to decompose. * @param column_order The per-column order if using output with lexicographic comparison From 70abc3ca97452b98e675b80dbc481d55dc0e0e78 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 24 Mar 2023 10:25:43 -0700 Subject: [PATCH 078/140] Fix depth comparision --- cpp/include/cudf/table/experimental/row_operators.cuh | 2 +- cpp/src/table/row_operators.cu | 5 +---- cpp/tests/sort/sort_nested_types_tests.cpp | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 846bebfe10b..a549e725605 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -397,7 +397,7 @@ class device_row_comparator { } if (lcol.num_child_columns() == 0) { - return cuda::std::pair(weak_ordering::EQUIVALENT, depth); + return cuda::std::pair(weak_ordering::EQUIVALENT, std::numeric_limits::max()); } // Non-empty structs have been modified to only have 1 child when using this. diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 30f8f89fd21..7be2b22ee6e 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -193,12 +193,9 @@ auto decompose_structs(table_view table, // lists column. // In such cases, the last column of the current branch will be `Struct` and // it will be modified to empty struct type `Struct<>` later on. - // In addition, the new cut out branch will be set to have the same depth as the - // current branch instead of higher depth. By this way, we can avoid the new branch - // to be ignored in `device_row_comparator` when the current branch has zero child. if (child_idx > 0 || (child_idx == 0 && c->children[0]->type().id() == type_id::LIST)) { - verticalized_col_depths.push_back(child_idx == 0 ? depth : depth + 1); + verticalized_col_depths.push_back(depth + 1); branch = &flattened.emplace_back(); } recursive_child(c->children[child_idx].get(), branch, depth + 1); diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index 8702887f5e3..f9235974167 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -91,7 +91,7 @@ TEST_F(NestedStructTest, StructsHaveListsNoNulls) } { - auto const expected_order = int32s_col{2, 0, 3, 4, 1, 5, 6}; + auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); @@ -115,7 +115,7 @@ TEST_F(NestedStructTest, StructsHaveListsWithNulls) } { - auto const expected_order = int32s_col{3, 0, 4, 5, 1, 6, 7, 2}; + auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); From 5ab1b0f76f00faeb868f4d5fcc7d3b931c6d9315 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 4 Apr 2023 13:51:02 -0700 Subject: [PATCH 079/140] Add `type_id` to check for element comparator --- .../cudf/table/experimental/row_operators.cuh | 49 ++++++++++++------- cpp/src/table/row_operators.cu | 39 ++++++++++++--- 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 2daf711163b..98e120a9b9c 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -155,6 +155,16 @@ using rhs_iterator = strong_index_iterator; namespace lexicographic { +/** + * @brief The base class for element comparator. + * + * This base class also provide a unique id for each derived class through the `type_id()` function. + */ +template +struct element_comparator_impl { + static inline std::uintptr_t type_id() { return reinterpret_cast(&type_id); } +}; + /** * @brief Computes a weak ordering of two values with special sorting behavior. * @@ -162,7 +172,7 @@ namespace lexicographic { * elements like lists, strings, or structs. It evaluates `NaN` as not less than, equal to, or * greater than other values and is IEEE-754 compliant. */ -struct physical_element_comparator { +struct physical_element_comparator : element_comparator_impl { /** * @brief Operator for relational comparisons. * @@ -182,7 +192,8 @@ struct physical_element_comparator { * elements like lists, strings, or structs. It evaluates `NaN` as equivalent to other `NaN`s and * greater than all other values. */ -struct sorting_physical_element_comparator { +struct sorting_physical_element_comparator + : element_comparator_impl { /** * @brief Operator for relational comparison of non-floating point values. * @@ -686,6 +697,9 @@ struct preprocessed_table { * Sets up the table for use with lexicographical comparison. The resulting preprocessed table can * be passed to the constructor of `lexicographic::self_comparator` to avoid preprocessing again. * + * @tparam PhysicalElementComparator A relational comparator functor that compares individual + * values rather than logical elements, defaults to `NaN` aware relational comparator that + * evaluates `NaN` as greater than all other values. * @param table The table to preprocess * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns are @@ -696,6 +710,7 @@ struct preprocessed_table { * @param stream The stream to launch kernels and h->d copies on while preprocessing. * @return A shared pointer to a preprocessed table */ + template static std::shared_ptr create(table_view const& table, host_span column_order, host_span null_precedence, @@ -741,7 +756,8 @@ struct preprocessed_table { std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& structs_ranked_columns); + std::vector>&& structs_ranked_columns, + std::uintptr_t element_comparator_type_id); preprocessed_table( table_device_view_owner&& table, @@ -749,7 +765,8 @@ struct preprocessed_table { rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& structs_ranked_columns); + std::vector>&& structs_ranked_columns, + std::uintptr_t element_comparator_type_id); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -822,6 +839,9 @@ struct preprocessed_table { // Intermediate columns generated from transforming the child columns of lists-of-structs columns // into integer columns using `cudf::rank()`, also need to be kept alive. std::vector> _structs_ranked_columns; + + // type_id of the element comparator used to preprocess the input table. + std::uintptr_t _element_comparator_type_id; }; /** @@ -856,10 +876,7 @@ class self_comparator { self_comparator(table_view const& t, host_span column_order = {}, host_span null_precedence = {}, - rmm::cuda_stream_view stream = cudf::get_default_stream()) - : d_t{preprocessed_table::create(t, column_order, null_precedence, stream)} - { - } + rmm::cuda_stream_view stream = cudf::get_default_stream()); /** * @brief Construct an owning object for performing a lexicographic comparison between two rows of @@ -902,11 +919,9 @@ class self_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - if constexpr (!std::is_same_v) { - CUDF_EXPECTS( - d_t->_structs_ranked_columns.size() == 0, - "The input table was preprocessed using a different type of physical element comparator."); - } + CUDF_EXPECTS( + d_t->_element_comparator_type_id == PhysicalElementComparator::type_id(), + "The input table was preprocessed using a different type of physical element comparator."); return less_comparator{ device_row_comparator{ @@ -927,11 +942,9 @@ class self_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - if constexpr (!std::is_same_v) { - CUDF_EXPECTS( - d_t->_structs_ranked_columns.size() == 0, - "The input table was preprocessed using a different type of physical element comparator."); - } + CUDF_EXPECTS( + d_t->_element_comparator_type_id == PhysicalElementComparator::type_id(), + "The input table was preprocessed using a different type of physical element comparator."); return less_equivalent_comparator{ device_row_comparator{ diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 3405cc31140..971c7b660d0 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -515,6 +515,7 @@ flatten_nested_structs_of_lists(table_view const& input, } // namespace +template std::shared_ptr preprocessed_table::create( table_view const& t, host_span column_order, @@ -554,7 +555,8 @@ std::shared_ptr preprocessed_table::create( std::move(dremel_data), std::move(d_dremel_device_view), std::move(flattened_t_aux_data), - std::move(structs_ranked_columns))); + std::move(structs_ranked_columns), + PhysicalElementComparator::type_id())); } else { return std::shared_ptr( new preprocessed_table(std::move(d_t), @@ -562,10 +564,23 @@ std::shared_ptr preprocessed_table::create( std::move(d_null_precedence), std::move(d_depths), std::move(flattened_t_aux_data), - std::move(structs_ranked_columns))); + std::move(structs_ranked_columns), + PhysicalElementComparator::type_id())); } } +template std::shared_ptr +preprocessed_table::create(table_view const&, + host_span, + host_span, + rmm::cuda_stream_view); + +template std::shared_ptr +preprocessed_table::create(table_view const&, + host_span, + host_span, + rmm::cuda_stream_view); + preprocessed_table::preprocessed_table( table_device_view_owner&& table, rmm::device_uvector&& column_order, @@ -574,7 +589,8 @@ preprocessed_table::preprocessed_table( std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& structs_ranked_columns) + std::vector>&& structs_ranked_columns, + std::uintptr_t element_comparator_type_id) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -582,7 +598,8 @@ preprocessed_table::preprocessed_table( _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), _flattened_input_aux_data(std::move(flattened_input_aux_data)), - _structs_ranked_columns(std::move(structs_ranked_columns)) + _structs_ranked_columns(std::move(structs_ranked_columns)), + _element_comparator_type_id(element_comparator_type_id) { } @@ -592,7 +609,8 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::unique_ptr&& flattened_input_aux_data, - std::vector>&& structs_ranked_columns) + std::vector>&& structs_ranked_columns, + std::uintptr_t element_comparator_type_id) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -600,7 +618,16 @@ preprocessed_table::preprocessed_table( _dremel_data{}, _dremel_device_views{}, _flattened_input_aux_data(std::move(flattened_input_aux_data)), - _structs_ranked_columns(std::move(structs_ranked_columns)) + _structs_ranked_columns(std::move(structs_ranked_columns)), + _element_comparator_type_id(element_comparator_type_id) +{ +} + +self_comparator::self_comparator(table_view const& t, + host_span column_order, + host_span null_precedence, + rmm::cuda_stream_view stream) + : d_t{preprocessed_table::create(t, column_order, null_precedence, stream)} { } From 104aeba799702ece8c063abd6e7b43d40d0d50da Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 7 Apr 2023 10:07:06 -0700 Subject: [PATCH 080/140] Add comment --- cpp/src/table/row_operators.cu | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 7be2b22ee6e..528b1fe1f79 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -95,7 +95,9 @@ table_view remove_struct_child_offsets(table_view table) * Special handling is needed in the cases of structs column having lists as its first child. In * such situations, the function decomposes the tree of N leaves into N+1 linear trees in which the * second tree was generated by extracting out leaf of the first tree. This is to make sure there is - * no structs column having child lists column in the output. + * no structs column having child lists column in the output. Note that structs with lists children + * in subsequent positions do not require any special treatment because the struct parent will be + * pruned for all subsequent children. * * For example, if the original table has a column `Struct, decimal>`, * From 1b8369a5f1b13157e80d6609d264972079966f42 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 7 Apr 2023 10:21:13 -0700 Subject: [PATCH 081/140] Add tests with sliced input --- cpp/tests/sort/sort_nested_types_tests.cpp | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index f9235974167..4075a8456d8 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -122,6 +122,70 @@ TEST_F(NestedStructTest, StructsHaveListsWithNulls) } } +TEST_F(NestedStructTest, SlicedStructsHaveListsNoNulls) +{ + // Input has equal elements, thus needs to be tested by stable sort. + // The original input has 3 first elements repeated at the beginning and the end. + auto const input_original = [] { + auto child0 = int32s_lists{ + {4, 2, 0}, {}, {5}, {4, 2, 0}, {}, {5}, {4, 1}, {4, 0}, {}, {}, {4, 2, 0}, {}, {5}}; + auto child1 = int32s_col{1, 2, 5, 1, 2, 5, 0, 3, 3, 4, 1, 2, 5}; + return structs_col{{child0, child1}}; + }(); + + auto const input = cudf::slice(input_original, {3, 10})[0]; + + { + auto const expected_order = int32s_col{1, 5, 6, 4, 3, 0, 2}; + auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{2, 0, 3, 4, 6, 5, 1}; + auto const order = + cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + +TEST_F(NestedStructTest, SlicedStructsHaveListsWithNulls) +{ + // Input has equal elements, thus needs to be tested by stable sort. + // The original input has 2 first elements repeated at the beginning and the end. + auto const input_original = [] { + auto child0 = int32s_lists{{{4, 2, null}, null_at(2)}, + {}, + {{4, 2, null}, null_at(2)}, + {}, + {} /*NULL*/, + {5}, + {4, 1}, + {4, 0}, + {}, + {}, + {{4, 2, null}, null_at(2)}, + {}}; + auto child1 = int32s_col{{1, 2, 1, 2, null, 5, null, 3, 3, 4, 1, 2}, nulls_at({4, 6})}; + return structs_col{{child0, child1}, null_at(4)}; + }(); + + auto const input = cudf::slice(input_original, {2, 10})[0]; + + { + auto const expected_order = int32s_col{2, 1, 6, 7, 5, 4, 0, 3}; + auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{3, 0, 4, 5, 7, 6, 1, 2}; + auto const order = + cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + TEST_F(NestedStructTest, StructsOfStructsHaveListsNoNulls) { // Input has equal elements, thus needs to be tested by stable sort. From 654a232db0c0fef6f1d2ceb7d595bdcfc04a6cca Mon Sep 17 00:00:00 2001 From: Nghia Truong <7416935+ttnghia@users.noreply.github.com> Date: Fri, 7 Apr 2023 11:26:29 -0600 Subject: [PATCH 082/140] Update cpp/src/table/row_operators.cu Co-authored-by: Bradley Dice --- cpp/src/table/row_operators.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 528b1fe1f79..f1bcc3f7d3d 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -155,7 +155,7 @@ table_view remove_struct_child_offsets(table_view table) * The list parents are still needed to define the range of elements in the leaf that belong to the * same row. * - * In the case of structs column having its first child is a lists column such as + * In the case of structs column with a lists column as its first child such as * `Struct, float>`, after decomposition we get three columns `Struct<>`, * `List`, and `float`. * From 9d7921d80e47a043f9bdb43c62983f6558ddd51d Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 7 Apr 2023 10:31:22 -0700 Subject: [PATCH 083/140] Optimize condition, and remove `UNKNOWN_NULL_COUNT` --- cpp/src/table/row_operators.cu | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index f1bcc3f7d3d..94636f5e6ca 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -195,8 +195,7 @@ auto decompose_structs(table_view table, // lists column. // In such cases, the last column of the current branch will be `Struct` and // it will be modified to empty struct type `Struct<>` later on. - if (child_idx > 0 || - (child_idx == 0 && c->children[0]->type().id() == type_id::LIST)) { + if (child_idx > 0 || c->children[0]->type().id() == type_id::LIST) { verticalized_col_depths.push_back(depth + 1); branch = &flattened.emplace_back(); } @@ -218,7 +217,7 @@ auto decompose_structs(table_view table, temp_col.size(), temp_col.head(), temp_col.null_mask(), - UNKNOWN_NULL_COUNT, + temp_col.null_count(), temp_col.offset(), {}); } @@ -235,7 +234,7 @@ auto decompose_structs(table_view table, prev_col.size(), nullptr, prev_col.null_mask(), - UNKNOWN_NULL_COUNT, + prev_col.null_count(), prev_col.offset(), std::move(children)); } @@ -249,7 +248,7 @@ auto decompose_structs(table_view table, parent->size(), nullptr, // list has no data of its own nullptr, // If we're going through this then nullmask is already in another branch - UNKNOWN_NULL_COUNT, + 0, parent->offset(), {*parent->children[lists_column_view::offsets_column_index], temp_col}); } else if (parent->type().id() == type_id::STRUCT) { @@ -258,7 +257,7 @@ auto decompose_structs(table_view table, parent->size(), temp_col.head(), temp_col.null_mask(), - UNKNOWN_NULL_COUNT, + temp_col.null_count(), parent->offset(), {temp_col.child_begin(), temp_col.child_end()}); } From cc446216617a4e11ac7358c02acb60400ecacb90 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 7 Apr 2023 19:36:52 -0700 Subject: [PATCH 084/140] Remove stale implementation --- .../cudf/table/experimental/row_operators.cuh | 46 ++++--------- cpp/src/table/row_operators.cu | 68 +------------------ 2 files changed, 17 insertions(+), 97 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 98e120a9b9c..bde3d671e53 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -54,11 +54,6 @@ #include #include -// Forward declaration to reduce dependency on the relevant header. -namespace cudf::structs::detail { -class flattened_table; -} - namespace cudf { namespace experimental { @@ -741,32 +736,25 @@ struct preprocessed_table { * contain an empty `dremel_device_view`. As such, this uvector has as many elements as there are * columns in the table (unlike the `dremel_data` parameter, which is only as long as the number * of list columns). - * @param flattened_input_aux_data The data structure generated from - * `cudf::structs::detail::flatten_nested_columns` containing the input flattened table along - * with its corresponding flattened column orders and null orders. * @param structs_ranked_columns Store the intermediate results from transforming the * child columns of lists-of-structs columns into integer columns using `cudf::rank()` * and will be used for row comparison. */ - preprocessed_table( - table_device_view_owner&& table, - rmm::device_uvector&& column_order, - rmm::device_uvector&& null_precedence, - rmm::device_uvector&& depths, - std::vector&& dremel_data, - rmm::device_uvector&& dremel_device_views, - std::unique_ptr&& flattened_input_aux_data, - std::vector>&& structs_ranked_columns, - std::uintptr_t element_comparator_type_id); - - preprocessed_table( - table_device_view_owner&& table, - rmm::device_uvector&& column_order, - rmm::device_uvector&& null_precedence, - rmm::device_uvector&& depths, - std::unique_ptr&& flattened_input_aux_data, - std::vector>&& structs_ranked_columns, - std::uintptr_t element_comparator_type_id); + preprocessed_table(table_device_view_owner&& table, + rmm::device_uvector&& column_order, + rmm::device_uvector&& null_precedence, + rmm::device_uvector&& depths, + std::vector&& dremel_data, + rmm::device_uvector&& dremel_device_views, + std::vector>&& structs_ranked_columns, + std::uintptr_t element_comparator_type_id); + + preprocessed_table(table_device_view_owner&& table, + rmm::device_uvector&& column_order, + rmm::device_uvector&& null_precedence, + rmm::device_uvector&& depths, + std::vector>&& structs_ranked_columns, + std::uintptr_t element_comparator_type_id); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -832,10 +820,6 @@ struct preprocessed_table { std::optional> _dremel_data; std::optional> _dremel_device_views; - // Auxiliary data generated from `cudf::structs::detail::flatten_nested_columns` - // that needs to be kept alive. - std::unique_ptr _flattened_input_aux_data; - // Intermediate columns generated from transforming the child columns of lists-of-structs columns // into integer columns using `cudf::rank()`, also need to be kept alive. std::vector> _structs_ranked_columns; diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index beba9d232ce..c9209123db4 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -463,57 +463,6 @@ std::pair>> transform_lists_of_s return {table_view{transformed_columns}, std::move(aux_data)}; } -/** - * @brief Check if the input column has structs-of-lists column at any nested level. - * - * @param input The input column to check - * @return Boolean value indicating if there is structs-of-lists column - */ -bool has_nested_structs_of_lists(column_view const& input) -{ - if (input.type().id() == type_id::STRUCT) { - return std::any_of(input.child_begin(), input.child_end(), [](auto const& child) { - return child.type().id() == type_id::LIST || has_nested_structs_of_lists(child); - }); - } else if (input.type().id() == type_id::LIST) { - return has_nested_structs_of_lists(input.child(lists_column_view::child_column_index)); - } - - return false; -} - -/** - * @brief Flatten the given table if it contains any structs-of-lists column. - * - * If the input table contains any structs-of-lists column, the entire table will be flattened to - * a table of non-struct columns. Otherwise, the input table is passed through. - * - * @param input The input table - * @param stream The stream to launch kernels and h->d copies on while preprocessing - * @return A pair of table_view representing the flattened input and an auxiliary data structure - * that needs to be kept alive - */ -std::pair> -flatten_nested_structs_of_lists(table_view const& input, - host_span column_order, - host_span null_precedence, - rmm::cuda_stream_view stream) -{ - if (std::any_of(input.begin(), input.end(), has_nested_structs_of_lists)) { - auto structs_flattened = cudf::structs::detail::flatten_nested_columns( - input, - std::vector{column_order.begin(), column_order.end()}, - std::vector{null_precedence.begin(), null_precedence.end()}, - cudf::structs::detail::column_nullability::FORCE, - stream, - rmm::mr::get_current_device_resource()); - auto output_table = structs_flattened->flattened_columns(); - return {std::move(output_table), std::move(structs_flattened)}; - } - - return {input, nullptr}; -} - } // namespace template @@ -523,20 +472,13 @@ std::shared_ptr preprocessed_table::create( host_span null_precedence, rmm::cuda_stream_view stream) { - // Firstly, flatten the input table if it contains any structs-of-lists column. - auto [flattened_t, flattened_t_aux_data] = - flatten_nested_structs_of_lists(t, column_order, null_precedence, stream); - // Next, transform any (nested) lists-of-structs column into lists-of-integers column. - auto [transformed_t, structs_ranked_columns] = transform_lists_of_structs(flattened_t, stream); + auto [transformed_t, structs_ranked_columns] = transform_lists_of_structs(t, stream); check_lex_compatibility(transformed_t); auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = - flattened_t_aux_data - ? decompose_structs( - transformed_t, flattened_t_aux_data->orders(), flattened_t_aux_data->null_orders()) - : decompose_structs(transformed_t, column_order, null_precedence); + decompose_structs(transformed_t, column_order, null_precedence); auto d_t = table_device_view::create(verticalized_lhs, stream); auto d_column_order = detail::make_device_uvector_async( @@ -555,7 +497,6 @@ std::shared_ptr preprocessed_table::create( std::move(d_depths), std::move(dremel_data), std::move(d_dremel_device_view), - std::move(flattened_t_aux_data), std::move(structs_ranked_columns), PhysicalElementComparator::type_id())); } else { @@ -564,7 +505,6 @@ std::shared_ptr preprocessed_table::create( std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), - std::move(flattened_t_aux_data), std::move(structs_ranked_columns), PhysicalElementComparator::type_id())); } @@ -589,7 +529,6 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::unique_ptr&& flattened_input_aux_data, std::vector>&& structs_ranked_columns, std::uintptr_t element_comparator_type_id) : _t(std::move(table)), @@ -598,7 +537,6 @@ preprocessed_table::preprocessed_table( _depths(std::move(depths)), _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), - _flattened_input_aux_data(std::move(flattened_input_aux_data)), _structs_ranked_columns(std::move(structs_ranked_columns)), _element_comparator_type_id(element_comparator_type_id) { @@ -609,7 +547,6 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::unique_ptr&& flattened_input_aux_data, std::vector>&& structs_ranked_columns, std::uintptr_t element_comparator_type_id) : _t(std::move(table)), @@ -618,7 +555,6 @@ preprocessed_table::preprocessed_table( _depths(std::move(depths)), _dremel_data{}, _dremel_device_views{}, - _flattened_input_aux_data(std::move(flattened_input_aux_data)), _structs_ranked_columns(std::move(structs_ranked_columns)), _element_comparator_type_id(element_comparator_type_id) { From 292985bb7918faa6322bd2c7f00cfac77b84352b Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 7 Apr 2023 19:50:40 -0700 Subject: [PATCH 085/140] Remove type id from element comparator --- .../cudf/table/experimental/row_operators.cuh | 49 +++++++------------ cpp/src/table/row_operators.cu | 39 +++------------ 2 files changed, 24 insertions(+), 64 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index bde3d671e53..178f5049b74 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -150,16 +150,6 @@ using rhs_iterator = strong_index_iterator; namespace lexicographic { -/** - * @brief The base class for element comparator. - * - * This base class also provide a unique id for each derived class through the `type_id()` function. - */ -template -struct element_comparator_impl { - static inline std::uintptr_t type_id() { return reinterpret_cast(&type_id); } -}; - /** * @brief Computes a weak ordering of two values with special sorting behavior. * @@ -167,7 +157,7 @@ struct element_comparator_impl { * elements like lists, strings, or structs. It evaluates `NaN` as not less than, equal to, or * greater than other values and is IEEE-754 compliant. */ -struct physical_element_comparator : element_comparator_impl { +struct physical_element_comparator { /** * @brief Operator for relational comparisons. * @@ -187,8 +177,7 @@ struct physical_element_comparator : element_comparator_impl { +struct sorting_physical_element_comparator { /** * @brief Operator for relational comparison of non-floating point values. * @@ -692,9 +681,6 @@ struct preprocessed_table { * Sets up the table for use with lexicographical comparison. The resulting preprocessed table can * be passed to the constructor of `lexicographic::self_comparator` to avoid preprocessing again. * - * @tparam PhysicalElementComparator A relational comparator functor that compares individual - * values rather than logical elements, defaults to `NaN` aware relational comparator that - * evaluates `NaN` as greater than all other values. * @param table The table to preprocess * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns are @@ -705,7 +691,6 @@ struct preprocessed_table { * @param stream The stream to launch kernels and h->d copies on while preprocessing. * @return A shared pointer to a preprocessed table */ - template static std::shared_ptr create(table_view const& table, host_span column_order, host_span null_precedence, @@ -746,15 +731,13 @@ struct preprocessed_table { rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::vector>&& structs_ranked_columns, - std::uintptr_t element_comparator_type_id); + std::vector>&& structs_ranked_columns); preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::vector>&& structs_ranked_columns, - std::uintptr_t element_comparator_type_id); + std::vector>&& structs_ranked_columns); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -823,9 +806,6 @@ struct preprocessed_table { // Intermediate columns generated from transforming the child columns of lists-of-structs columns // into integer columns using `cudf::rank()`, also need to be kept alive. std::vector> _structs_ranked_columns; - - // type_id of the element comparator used to preprocess the input table. - std::uintptr_t _element_comparator_type_id; }; /** @@ -860,7 +840,10 @@ class self_comparator { self_comparator(table_view const& t, host_span column_order = {}, host_span null_precedence = {}, - rmm::cuda_stream_view stream = cudf::get_default_stream()); + rmm::cuda_stream_view stream = cudf::get_default_stream()) + : d_t{preprocessed_table::create(t, column_order, null_precedence, stream)} + { + } /** * @brief Construct an owning object for performing a lexicographic comparison between two rows of @@ -903,9 +886,11 @@ class self_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - CUDF_EXPECTS( - d_t->_element_comparator_type_id == PhysicalElementComparator::type_id(), - "The input table was preprocessed using a different type of physical element comparator."); + if constexpr (!std::is_same_v) { + CUDF_EXPECTS( + d_t->_structs_ranked_columns.size() == 0, + "The input table was preprocessed using a different type of physical element comparator."); + } return less_comparator{ device_row_comparator{ @@ -926,9 +911,11 @@ class self_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - CUDF_EXPECTS( - d_t->_element_comparator_type_id == PhysicalElementComparator::type_id(), - "The input table was preprocessed using a different type of physical element comparator."); + if constexpr (!std::is_same_v) { + CUDF_EXPECTS( + d_t->_structs_ranked_columns.size() == 0, + "The input table was preprocessed using a different type of physical element comparator."); + } return less_equivalent_comparator{ device_row_comparator{ diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index c9209123db4..e294ba44dc8 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -465,7 +465,6 @@ std::pair>> transform_lists_of_s } // namespace -template std::shared_ptr preprocessed_table::create( table_view const& t, host_span column_order, @@ -497,31 +496,17 @@ std::shared_ptr preprocessed_table::create( std::move(d_depths), std::move(dremel_data), std::move(d_dremel_device_view), - std::move(structs_ranked_columns), - PhysicalElementComparator::type_id())); + std::move(structs_ranked_columns))); } else { return std::shared_ptr( new preprocessed_table(std::move(d_t), std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), - std::move(structs_ranked_columns), - PhysicalElementComparator::type_id())); + std::move(structs_ranked_columns))); } } -template std::shared_ptr -preprocessed_table::create(table_view const&, - host_span, - host_span, - rmm::cuda_stream_view); - -template std::shared_ptr -preprocessed_table::create(table_view const&, - host_span, - host_span, - rmm::cuda_stream_view); - preprocessed_table::preprocessed_table( table_device_view_owner&& table, rmm::device_uvector&& column_order, @@ -529,16 +514,14 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::vector>&& structs_ranked_columns, - std::uintptr_t element_comparator_type_id) + std::vector>&& structs_ranked_columns) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), - _structs_ranked_columns(std::move(structs_ranked_columns)), - _element_comparator_type_id(element_comparator_type_id) + _structs_ranked_columns(std::move(structs_ranked_columns)) { } @@ -547,24 +530,14 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::vector>&& structs_ranked_columns, - std::uintptr_t element_comparator_type_id) + std::vector>&& structs_ranked_columns) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data{}, _dremel_device_views{}, - _structs_ranked_columns(std::move(structs_ranked_columns)), - _element_comparator_type_id(element_comparator_type_id) -{ -} - -self_comparator::self_comparator(table_view const& t, - host_span column_order, - host_span null_precedence, - rmm::cuda_stream_view stream) - : d_t{preprocessed_table::create(t, column_order, null_precedence, stream)} + _structs_ranked_columns(std::move(structs_ranked_columns)) { } From befa5d0cb6040a21502cf6f1b6df93ff893b902f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 7 Apr 2023 21:22:52 -0700 Subject: [PATCH 086/140] TMP --- .../cudf/table/experimental/row_operators.cuh | 1 + cpp/src/table/row_operators.cu | 52 +++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index cbeac38920e..da65eb29212 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -745,6 +745,7 @@ struct preprocessed_table { */ static std::shared_ptr create_preprocessed_table( table_view const& input, + std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index dc91412ceee..ba4828b850e 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -324,6 +324,8 @@ void check_lex_compatibility(table_view const& input) check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { + CUDF_EXPECTS(child->type().id() != type_id::LIST, + "Cannot lexicographic compare a table with a STRUCT of LIST column"); check_column(*child); } } @@ -524,6 +526,7 @@ transform_lists_of_structs(table_view const& lhs, std::shared_ptr preprocessed_table::create_preprocessed_table( table_view const& input, + std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, @@ -532,19 +535,16 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl { check_lex_compatibility(input); - auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = - decompose_structs(input, column_order, null_precedence); - - auto d_t = table_device_view::create(verticalized_lhs, stream); - auto d_column_order = detail::make_device_uvector_async( - new_column_order, stream, rmm::mr::get_current_device_resource()); + auto d_t = table_device_view::create(input, stream); + auto d_column_order = + detail::make_device_uvector_async(column_order, stream, rmm::mr::get_current_device_resource()); auto d_null_precedence = detail::make_device_uvector_async( - new_null_precedence, stream, rmm::mr::get_current_device_resource()); + null_precedence, stream, rmm::mr::get_current_device_resource()); auto d_depths = detail::make_device_uvector_async( verticalized_col_depths, stream, rmm::mr::get_current_device_resource()); if (detail::has_nested_columns(input)) { - auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(verticalized_lhs, stream); + auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(input, stream); return std::shared_ptr( new preprocessed_table(std::move(d_t), std::move(d_column_order), @@ -566,23 +566,28 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl } std::shared_ptr preprocessed_table::create( - table_view const& t, + table_view const& input, host_span column_order, host_span null_precedence, rmm::cuda_stream_view stream) { + auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = + decompose_structs(input, column_order, null_precedence); + // Next, transform any (nested) lists-of-structs column into lists-of-integers column. [[maybe_unused]] auto [transformed_t, unused_0, structs_ranked_columns, unused_1] = - transform_lists_of_structs(t, std::nullopt, stream); + transform_lists_of_structs(decomposed_input, std::nullopt, stream); // Since the preprocessed_table is created alone, it is safe for two-table comparator // only if not any transformation for lists-of-structs was performed. + // TODO: this should also based on whether structs have floating point. bool const safe_for_two_table_comparator = structs_ranked_columns.size() == 0; return create_preprocessed_table(transformed_t, + std::move(verticalized_col_depths), std::move(structs_ranked_columns), - column_order, - null_precedence, + new_column_order, + new_null_precedence, safe_for_two_table_comparator, stream); } @@ -594,22 +599,35 @@ preprocessed_table::create(table_view const& lhs, host_span null_precedence, rmm::cuda_stream_view stream) { + auto [decomposed_lhs, + new_column_order_lhs, + new_null_precedence_lhs, + verticalized_col_depths_lhs] = decompose_structs(lhs, column_order, null_precedence); + + auto [decomposed_rhs, + new_column_order_rhs, + new_null_precedence_rhs, + verticalized_col_depths_rhs] = decompose_structs(rhs, column_order, null_precedence); + // Transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_lhs, transformed_rhs_opt, structs_ranked_columns_lhs, - structs_ranked_columns_rhs] = transform_lists_of_structs(lhs, rhs, stream); + structs_ranked_columns_rhs] = + transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); return {create_preprocessed_table(transformed_lhs, + std::move(verticalized_col_depths_lhs), std::move(structs_ranked_columns_lhs), - column_order, - null_precedence, + new_column_order_lhs, + new_null_precedence_lhs, true /*safe_for_two_table_comparator*/, stream), create_preprocessed_table(transformed_rhs_opt.value(), + std::move(verticalized_col_depths_rhs), std::move(structs_ranked_columns_rhs), - column_order, - null_precedence, + new_column_order_rhs, + new_null_precedence_rhs, true /*safe_for_two_table_comparator*/, stream)}; } From 1795dfc4ca847a2deec6a4f469260b9d9bdb4d52 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 7 Apr 2023 21:27:14 -0700 Subject: [PATCH 087/140] Revert "TMP" This reverts commit befa5d0cb6040a21502cf6f1b6df93ff893b902f. --- .../cudf/table/experimental/row_operators.cuh | 1 - cpp/src/table/row_operators.cu | 63 +++++++------------ 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index da65eb29212..cbeac38920e 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -745,7 +745,6 @@ struct preprocessed_table { */ static std::shared_ptr create_preprocessed_table( table_view const& input, - std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index ba4828b850e..c3554da0c8d 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -472,11 +472,12 @@ std:: } } } else if (lhs.type().id() == type_id::STRUCT) { - CUDF_EXPECTS( - std::all_of(lhs.child_begin(), - lhs.child_end(), - [](auto const& child) { return child.type().id() != type_id::LIST; }), - "Structs columns containing lists should be flattened before reaching this function."); + // CUDF_EXPECTS( + // std::all_of(lhs.child_begin(), + // lhs.child_end(), + // [](auto const& child) { return child.type().id() != type_id::LIST; }), + // "Structs columns containing lists should be flattened before reaching this function."); + // TODO: check recursively. } return {lhs, rhs, nullptr, nullptr}; @@ -526,25 +527,27 @@ transform_lists_of_structs(table_view const& lhs, std::shared_ptr preprocessed_table::create_preprocessed_table( table_view const& input, - std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, bool safe_for_two_table_comparator, rmm::cuda_stream_view stream) { - check_lex_compatibility(input); + auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = + decompose_structs(input, column_order, null_precedence); + + check_lex_compatibility(verticalized_lhs); - auto d_t = table_device_view::create(input, stream); - auto d_column_order = - detail::make_device_uvector_async(column_order, stream, rmm::mr::get_current_device_resource()); + auto d_t = table_device_view::create(verticalized_lhs, stream); + auto d_column_order = detail::make_device_uvector_async( + new_column_order, stream, rmm::mr::get_current_device_resource()); auto d_null_precedence = detail::make_device_uvector_async( - null_precedence, stream, rmm::mr::get_current_device_resource()); + new_null_precedence, stream, rmm::mr::get_current_device_resource()); auto d_depths = detail::make_device_uvector_async( verticalized_col_depths, stream, rmm::mr::get_current_device_resource()); if (detail::has_nested_columns(input)) { - auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(input, stream); + auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(verticalized_lhs, stream); return std::shared_ptr( new preprocessed_table(std::move(d_t), std::move(d_column_order), @@ -566,28 +569,23 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl } std::shared_ptr preprocessed_table::create( - table_view const& input, + table_view const& t, host_span column_order, host_span null_precedence, rmm::cuda_stream_view stream) { - auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = - decompose_structs(input, column_order, null_precedence); - // Next, transform any (nested) lists-of-structs column into lists-of-integers column. [[maybe_unused]] auto [transformed_t, unused_0, structs_ranked_columns, unused_1] = - transform_lists_of_structs(decomposed_input, std::nullopt, stream); + transform_lists_of_structs(t, std::nullopt, stream); // Since the preprocessed_table is created alone, it is safe for two-table comparator // only if not any transformation for lists-of-structs was performed. - // TODO: this should also based on whether structs have floating point. bool const safe_for_two_table_comparator = structs_ranked_columns.size() == 0; return create_preprocessed_table(transformed_t, - std::move(verticalized_col_depths), std::move(structs_ranked_columns), - new_column_order, - new_null_precedence, + column_order, + null_precedence, safe_for_two_table_comparator, stream); } @@ -599,35 +597,22 @@ preprocessed_table::create(table_view const& lhs, host_span null_precedence, rmm::cuda_stream_view stream) { - auto [decomposed_lhs, - new_column_order_lhs, - new_null_precedence_lhs, - verticalized_col_depths_lhs] = decompose_structs(lhs, column_order, null_precedence); - - auto [decomposed_rhs, - new_column_order_rhs, - new_null_precedence_rhs, - verticalized_col_depths_rhs] = decompose_structs(rhs, column_order, null_precedence); - // Transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_lhs, transformed_rhs_opt, structs_ranked_columns_lhs, - structs_ranked_columns_rhs] = - transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); + structs_ranked_columns_rhs] = transform_lists_of_structs(lhs, rhs, stream); return {create_preprocessed_table(transformed_lhs, - std::move(verticalized_col_depths_lhs), std::move(structs_ranked_columns_lhs), - new_column_order_lhs, - new_null_precedence_lhs, + column_order, + null_precedence, true /*safe_for_two_table_comparator*/, stream), create_preprocessed_table(transformed_rhs_opt.value(), - std::move(verticalized_col_depths_rhs), std::move(structs_ranked_columns_rhs), - new_column_order_rhs, - new_null_precedence_rhs, + column_order, + null_precedence, true /*safe_for_two_table_comparator*/, stream)}; } From 4eabe1dc586d47ecbc877b64b33e1932c9587e6f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 8 Apr 2023 14:38:51 -0700 Subject: [PATCH 088/140] Stop decomposing column at lists column --- .../cudf/table/experimental/row_operators.cuh | 2 + cpp/src/table/row_operators.cu | 61 +++++++++++++------ cpp/tests/sort/sort_nested_types_tests.cpp | 2 + 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index cbeac38920e..a2491de9b84 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -734,6 +734,7 @@ struct preprocessed_table { friend class two_table_comparator; ///< Allow two_table_comparator to access private members /** + * TODO * @brief create_preprocessed_table * @param input * @param flattened_input_aux_data @@ -745,6 +746,7 @@ struct preprocessed_table { */ static std::shared_ptr create_preprocessed_table( table_view const& input, + std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index c3554da0c8d..a12f296f5f4 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -139,6 +139,8 @@ table_view remove_struct_child_offsets(table_view table) * When list columns are present, the decomposition is performed similarly to pure structs but list * parent columns are NOT pruned * + * // TODO: stop with list. + * * For example, if the original table has a column `List>`, * * L @@ -190,8 +192,9 @@ auto decompose_structs(table_view table, int depth) { branch->push_back(c); if (c->type().id() == type_id::LIST) { - recursive_child( - c->children[lists_column_view::child_column_index].get(), branch, depth + 1); + // recursive_child( + // c->children[lists_column_view::child_column_index].get(), branch, depth + // + 1); } else if (c->type().id() == type_id::STRUCT) { for (size_t child_idx = 0; child_idx < c->children.size(); ++child_idx) { // When child_idx == 0, we also cut off the current branch if its first child is a @@ -472,6 +475,11 @@ std:: } } } else if (lhs.type().id() == type_id::STRUCT) { + std::vector new_children_lhs; + std::vector new_children_rhs; + for (size_type child_idx = 0; child_idx < lhs.num_children(); ++child_idx) { + // + } // CUDF_EXPECTS( // std::all_of(lhs.child_begin(), // lhs.child_end(), @@ -527,27 +535,28 @@ transform_lists_of_structs(table_view const& lhs, std::shared_ptr preprocessed_table::create_preprocessed_table( table_view const& input, + std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, bool safe_for_two_table_comparator, rmm::cuda_stream_view stream) { - auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = - decompose_structs(input, column_order, null_precedence); + // auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = + // decompose_structs(input, column_order, null_precedence); - check_lex_compatibility(verticalized_lhs); + check_lex_compatibility(input); - auto d_t = table_device_view::create(verticalized_lhs, stream); - auto d_column_order = detail::make_device_uvector_async( - new_column_order, stream, rmm::mr::get_current_device_resource()); + auto d_t = table_device_view::create(input, stream); + auto d_column_order = + detail::make_device_uvector_async(column_order, stream, rmm::mr::get_current_device_resource()); auto d_null_precedence = detail::make_device_uvector_async( - new_null_precedence, stream, rmm::mr::get_current_device_resource()); + null_precedence, stream, rmm::mr::get_current_device_resource()); auto d_depths = detail::make_device_uvector_async( verticalized_col_depths, stream, rmm::mr::get_current_device_resource()); if (detail::has_nested_columns(input)) { - auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(verticalized_lhs, stream); + auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(input, stream); return std::shared_ptr( new preprocessed_table(std::move(d_t), std::move(d_column_order), @@ -569,23 +578,27 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl } std::shared_ptr preprocessed_table::create( - table_view const& t, + table_view const& input, host_span column_order, host_span null_precedence, rmm::cuda_stream_view stream) { + auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = + decompose_structs(input, column_order, null_precedence); + // Next, transform any (nested) lists-of-structs column into lists-of-integers column. [[maybe_unused]] auto [transformed_t, unused_0, structs_ranked_columns, unused_1] = - transform_lists_of_structs(t, std::nullopt, stream); + transform_lists_of_structs(decomposed_input, std::nullopt, stream); // Since the preprocessed_table is created alone, it is safe for two-table comparator // only if not any transformation for lists-of-structs was performed. bool const safe_for_two_table_comparator = structs_ranked_columns.size() == 0; return create_preprocessed_table(transformed_t, + std::move(verticalized_col_depths), std::move(structs_ranked_columns), - column_order, - null_precedence, + new_column_order, + new_null_precedence, safe_for_two_table_comparator, stream); } @@ -597,22 +610,32 @@ preprocessed_table::create(table_view const& lhs, host_span null_precedence, rmm::cuda_stream_view stream) { + auto [decomposed_lhs, + new_column_order_lhs, + new_null_precedence_lhs, + verticalized_col_depths_lhs] = decompose_structs(lhs, column_order, null_precedence); + [[maybe_unused]] auto [decomposed_rhs, unused0, unused1, verticalized_col_depths_rhs] = + decompose_structs(rhs, column_order, null_precedence); + // Transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_lhs, transformed_rhs_opt, structs_ranked_columns_lhs, - structs_ranked_columns_rhs] = transform_lists_of_structs(lhs, rhs, stream); + structs_ranked_columns_rhs] = + transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); return {create_preprocessed_table(transformed_lhs, + std::move(verticalized_col_depths_lhs), std::move(structs_ranked_columns_lhs), - column_order, - null_precedence, + new_column_order_lhs, + new_null_precedence_lhs, true /*safe_for_two_table_comparator*/, stream), create_preprocessed_table(transformed_rhs_opt.value(), + std::move(verticalized_col_depths_rhs), std::move(structs_ranked_columns_rhs), - column_order, - null_precedence, + new_column_order_lhs, + new_null_precedence_lhs, true /*safe_for_two_table_comparator*/, stream)}; } diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index 2300ef3be41..578bf5aa7ac 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -353,3 +353,5 @@ TEST_F(NestedListTest, ListsOfListsOfStructsNoNulls) CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } } + +// TODO: Struct of list of struct From 589ee83a2cff95606f4c4f7befe83cbf78abc02d Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 8 Apr 2023 19:14:43 -0700 Subject: [PATCH 089/140] Add test --- cpp/tests/sort/sort_nested_types_tests.cpp | 35 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index 578bf5aa7ac..2e8d3e77905 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -244,6 +244,39 @@ TEST_F(NestedStructTest, StructsOfStructsHaveListsWithNulls) } } +TEST_F(NestedStructTest, SimpleStructsOfListsOfStructsNoNulls) +{ + auto const input = [] { + auto const make_lists_of_structs = [] { + auto const get_structs = [] { + auto child0 = int32s_col{3, 2, 3, 3, 4, 2, 4, 4, 1, 0, 3, 0, 2, 5, 4}; + auto child1 = int32s_col{0, 4, 3, 2, 1, 1, 5, 1, 5, 5, 4, 2, 4, 1, 3}; + return structs_col{{child0, child1}}; + }; + return cudf::make_lists_column( + 8, int32s_col{0, 3, 5, 6, 6, 8, 10, 12, 15}.release(), get_structs().release(), 0, {}); + }; + + std::vector> children; + children.emplace_back(make_lists_of_structs()); + children.emplace_back(make_lists_of_structs()); + + return cudf::make_structs_column(8, std::move(children), 0, {}); + }(); + + { + auto const expected_order = int32s_col{3, 5, 2, 7, 0, 1, 6, 4}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{4, 6, 1, 0, 7, 2, 5, 3}; + auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + struct NestedListTest : public cudf::test::BaseFixture { }; @@ -353,5 +386,3 @@ TEST_F(NestedListTest, ListsOfListsOfStructsNoNulls) CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } } - -// TODO: Struct of list of struct From 2ca024cb1118d0e87ee7c3be84e0895fc8d24711 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 8 Apr 2023 19:16:19 -0700 Subject: [PATCH 090/140] Enable check for structs of lists --- cpp/src/table/row_operators.cu | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index a12f296f5f4..00bf71d50e1 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -475,17 +475,10 @@ std:: } } } else if (lhs.type().id() == type_id::STRUCT) { - std::vector new_children_lhs; - std::vector new_children_rhs; - for (size_type child_idx = 0; child_idx < lhs.num_children(); ++child_idx) { - // - } - // CUDF_EXPECTS( - // std::all_of(lhs.child_begin(), - // lhs.child_end(), - // [](auto const& child) { return child.type().id() != type_id::LIST; }), - // "Structs columns containing lists should be flattened before reaching this function."); - // TODO: check recursively. + CUDF_EXPECTS(std::all_of(lhs.child_begin(), + lhs.child_end(), + [](auto const& child) { return child.type().id() != type_id::LIST; }), + "Structs columns should be decomposed before reaching this function."); } return {lhs, rhs, nullptr, nullptr}; From a0151c01f47205eef09b12a69e57004981f3ef74 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 8 Apr 2023 19:23:34 -0700 Subject: [PATCH 091/140] Reverse changes to utilities.cpp --- cpp/src/structs/utilities.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cpp/src/structs/utilities.cpp b/cpp/src/structs/utilities.cpp index 72ab9429f27..d30349aaf9c 100644 --- a/cpp/src/structs/utilities.cpp +++ b/cpp/src/structs/utilities.cpp @@ -112,6 +112,7 @@ struct table_flattener { mr{mr} { superimpose_nulls(input); + fail_if_unsupported_types(input); } /** @@ -125,6 +126,12 @@ struct table_flattener { this->nullable_data = std::move(tmp_nullable_data); } + void fail_if_unsupported_types(table_view const& input) const + { + auto const has_lists = std::any_of(input.begin(), input.end(), is_or_has_nested_lists); + CUDF_EXPECTS(not has_lists, "Flattening LIST columns is not supported."); + } + // Convert null_mask to BOOL8 columns and flatten the struct children in order. void flatten_struct_column(structs_column_view const& col, order col_order, From 9f929b2846942b5f2e07c814fa0fa5c24f813c57 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Sat, 8 Apr 2023 19:46:09 -0700 Subject: [PATCH 092/140] Fix `decompose_structs` --- cpp/src/table/row_operators.cu | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 00bf71d50e1..ed8a6fd48a2 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include #include @@ -172,7 +174,8 @@ table_view remove_struct_child_offsets(table_view table) */ auto decompose_structs(table_view table, host_span column_order = {}, - host_span null_precedence = {}) + host_span null_precedence = {}, + bool decompose_lists = false) { auto linked_columns = detail::table_to_linked_columns(table); @@ -191,10 +194,9 @@ auto decompose_structs(table_view table, std::vector* branch, int depth) { branch->push_back(c); - if (c->type().id() == type_id::LIST) { - // recursive_child( - // c->children[lists_column_view::child_column_index].get(), branch, depth - // + 1); + if (c->type().id() == type_id::LIST && decompose_lists) { + recursive_child( + c->children[lists_column_view::child_column_index].get(), branch, depth + 1); } else if (c->type().id() == type_id::STRUCT) { for (size_t child_idx = 0; child_idx < c->children.size(); ++child_idx) { // When child_idx == 0, we also cut off the current branch if its first child is a @@ -610,6 +612,12 @@ preprocessed_table::create(table_view const& lhs, [[maybe_unused]] auto [decomposed_rhs, unused0, unused1, verticalized_col_depths_rhs] = decompose_structs(rhs, column_order, null_precedence); + // printf("line %d\n", __LINE__); + // cudf::test::print(decomposed_lhs.column(0)); + + // printf("line %d\n", __LINE__); + // cudf::test::print(decomposed_rhs.column(0)); + // Transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_lhs, transformed_rhs_opt, @@ -617,6 +625,12 @@ preprocessed_table::create(table_view const& lhs, structs_ranked_columns_rhs] = transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); + // printf("line %d\n", __LINE__); + // cudf::test::print(transformed_lhs.column(0)); + + // printf("line %d\n", __LINE__); + // cudf::test::print(transformed_rhs_opt.value().column(0)); + return {create_preprocessed_table(transformed_lhs, std::move(verticalized_col_depths_lhs), std::move(structs_ranked_columns_lhs), @@ -694,7 +708,7 @@ std::shared_ptr preprocessed_table::create(table_view const& auto [null_pushed_table, nullable_data] = structs::detail::push_down_nulls(t, stream, rmm::mr::get_current_device_resource()); auto struct_offset_removed_table = remove_struct_child_offsets(null_pushed_table); - auto verticalized_t = std::get<0>(decompose_structs(struct_offset_removed_table)); + auto verticalized_t = std::get<0>(decompose_structs(struct_offset_removed_table, {}, {}, true)); auto d_t = table_device_view_owner(table_device_view::create(verticalized_t, stream)); return std::shared_ptr(new preprocessed_table( From e25fbfcd7ede161e55eeaf3229a91b9fcaadd5e8 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Mon, 10 Apr 2023 10:45:14 -0700 Subject: [PATCH 093/140] Improve condition for `safe_for_two_table_comparator` --- cpp/src/table/row_operators.cu | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index ed8a6fd48a2..1b24ab24280 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -585,9 +585,17 @@ std::shared_ptr preprocessed_table::create( [[maybe_unused]] auto [transformed_t, unused_0, structs_ranked_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, stream); - // Since the preprocessed_table is created alone, it is safe for two-table comparator - // only if not any transformation for lists-of-structs was performed. - bool const safe_for_two_table_comparator = structs_ranked_columns.size() == 0; + // Since the input table is preprocessed alone, it is safe to use for comparing against other + // table in two-table comparator only if: + // - Not any transformation for lists-of-structs was performed, or. + // - The input column does not have any floating-point column at any nested level. + std::function const has_nested_floating_point = [&](auto const& col) { + return col.type().id() == type_id::FLOAT32 || col.type().id() == type_id::FLOAT64 || + std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); + }; + auto const safe_for_two_table_comparator = + structs_ranked_columns.size() == 0 || + std::none_of(input.begin(), input.end(), has_nested_floating_point); return create_preprocessed_table(transformed_t, std::move(verticalized_col_depths), From 4f87848f64a94483161110998b55495ef87beeef Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 11 Apr 2023 14:26:33 -0700 Subject: [PATCH 094/140] Rename parameter --- cpp/src/table/row_operators.cu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 0bfbc19a261..45a7b616ea3 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -527,7 +527,7 @@ transform_lists_of_structs(table_view const& lhs, } // namespace std::shared_ptr preprocessed_table::create_preprocessed_table( - table_view const& input, + table_view const& preprocessed_input, std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, @@ -538,9 +538,9 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl // auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = // decompose_structs(input, column_order, null_precedence); - check_lex_compatibility(input); + check_lex_compatibility(preprocessed_input); - auto d_t = table_device_view::create(input, stream); + auto d_t = table_device_view::create(preprocessed_input, stream); auto d_column_order = detail::make_device_uvector_async(column_order, stream, rmm::mr::get_current_device_resource()); auto d_null_precedence = detail::make_device_uvector_async( @@ -548,8 +548,8 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl auto d_depths = detail::make_device_uvector_async( verticalized_col_depths, stream, rmm::mr::get_current_device_resource()); - if (detail::has_nested_columns(input)) { - auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(input, stream); + if (detail::has_nested_columns(preprocessed_input)) { + auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(preprocessed_input, stream); return std::shared_ptr( new preprocessed_table(std::move(d_t), std::move(d_column_order), From 2cfd999c3e80f742e62fb7dfbb56788b508f5485 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 11 Apr 2023 14:37:36 -0700 Subject: [PATCH 095/140] Rename variable and rewrite docs --- .../cudf/table/experimental/row_operators.cuh | 94 ++++++++++--------- cpp/src/table/row_operators.cu | 22 ++--- 2 files changed, 62 insertions(+), 54 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index a2491de9b84..09207af2114 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -683,18 +683,18 @@ struct preprocessed_table { * `lexicographic::two_table_comparator` to avoid preprocessing again. * * Note that the output of this factory function cannot be used in `two_table_comparator` if the - * input table contains lists-of-structs. In such cases, please use the overload - * `preprocessed_table::create(table_view const&, table_view const&,...)`. + * input table contains lists-of-structs having floating-point children column. In such cases, + * please use the overload `preprocessed_table::create(table_view const&, table_view const&,...)`. * * @param table The table to preprocess * @param column_order Optional, host array the same length as a row that indicates the desired - * ascending/descending order of each column in a row. If empty, it is assumed all columns are - * sorted in ascending order. + * ascending/descending order of each column in a row. If empty, it is assumed all columns + * are sorted in ascending order. * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If it is nullptr, then null precedence would be - * `null_order::BEFORE` for all columns. + * values compare to all other for every column. If it is nullptr, then null precedence + * would be `null_order::BEFORE` for all columns. * @param stream The stream to launch kernels and h->d copies on while preprocessing. - * @return A shared pointer to a preprocessed table + * @return A shared pointer to a preprocessed table. */ static std::shared_ptr create(table_view const& table, host_span column_order, @@ -708,17 +708,17 @@ struct preprocessed_table { * can be passed to the constructor of `lexicographic::self_comparator` or * `lexicographic::two_table_comparator` to avoid preprocessing again. * - * This factory function performs some extra operations to guarantee that its output can be safely - * used in `two_table_comparator` for all cases. + * This factory function performs some extra operations to guarantee that its output can be used + * in `two_table_comparator` for all cases. * * @param lhs The lhs table to preprocess * @param rhs The rhs table to preprocess * @param column_order Optional, host array the same length as a row that indicates the desired - * ascending/descending order of each column in a row. If empty, it is assumed all columns are - * sorted in ascending order. + * ascending/descending order of each column in a row. If empty, it is assumed all columns + * are sorted in ascending order. * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If it is nullptr, then null precedence would be - * `null_order::BEFORE` for all columns. + * values compare to all other for every column. If it is nullptr, then null precedence + * would be `null_order::BEFORE` for all columns. * @param stream The stream to launch kernels and h->d copies on while preprocessing. * @return A pair of shared pointers to the preprocessed tables */ @@ -734,23 +734,30 @@ struct preprocessed_table { friend class two_table_comparator; ///< Allow two_table_comparator to access private members /** - * TODO - * @brief create_preprocessed_table - * @param input - * @param flattened_input_aux_data - * @param transformed_aux_data - * @param column_order - * @param null_precedence - * @param safe_for_two_table_comparator - * @param stream + * @brief Create the output preprocessed table from intermediate preprocessing results + * + * @param preprocessed_input The table resulted from preprocessing + * @param verticalized_col_depths The depths of each column resulting from decomposing struct + * columns in the original input table + * @param structs_ranked_columns Store the intermediate results from transforming the child + * columns of lists-of-structs columns into integer columns using `cudf::rank()` + * @param column_order Optional, host array the same length as a row that indicates the desired + * ascending/descending order of each column in a row. If empty, it is assumed all columns + * are sorted in ascending order. + * @param null_precedence Optional, device array the same length as a row and indicates how null + * values compare to all other for every column. If it is nullptr, then null precedence + * would be `null_order::BEFORE` for all columns. + * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform + * any lists-of-structs column having floating-point children using `cudf::rank`. + * @param stream The stream to launch kernels and h->d copies on while preprocessing. */ static std::shared_ptr create_preprocessed_table( - table_view const& input, + table_view const& preprocessed_input, std::vector&& verticalized_col_depths, std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, - bool safe_for_two_table_comparator, + bool ranked_floating_point, rmm::cuda_stream_view stream); /** @@ -761,24 +768,24 @@ struct preprocessed_table { * * @param table The table to preprocess * @param column_order Optional, device array the same length as a row that indicates the desired - * ascending/descending order of each column in a row. If empty, it is assumed all columns are - * sorted in ascending order. + * ascending/descending order of each column in a row. If empty, it is assumed all columns + * are sorted in ascending order. * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If it is nullptr, then null precedence would be - * `null_order::BEFORE` for all columns. + * values compare to all other for every column. If it is nullptr, then null precedence + * would be `null_order::BEFORE` for all columns. * @param depths The depths of each column resulting from decomposing struct columns. * @param dremel_data The dremel data for each list column. The length of this object is the - * number of list columns in the table. + * number of list columns in the table. * @param dremel_device_views Device views into the dremel_data structs contained in the - * `dremel_data` parameter. For columns that are not list columns, this uvector will should - * contain an empty `dremel_device_view`. As such, this uvector has as many elements as there are - * columns in the table (unlike the `dremel_data` parameter, which is only as long as the number - * of list columns). + * `dremel_data` parameter. For columns that are not list columns, this uvector will should + * contain an empty `dremel_device_view`. As such, this uvector has as many elements as + * there are columns in the table (unlike the `dremel_data` parameter, which is only as + * long as the number of list columns). * @param structs_ranked_columns Store the intermediate results from transforming the child - * columns of lists-of-structs columns into integer columns using `cudf::rank()` and will be used - * for row comparison. - * @param safe_for_two_table_comparator Flat indicating if the preprocessed table is safe to use - * for two-table comparator. + * columns of lists-of-structs columns into integer columns using `cudf::rank()` and will + * be used for row comparison. + * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform + * any lists-of-structs column having floating-point children using `cudf::rank`. */ preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, @@ -787,14 +794,14 @@ struct preprocessed_table { std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::vector>&& structs_ranked_columns, - bool safe_for_two_table_comparator); + bool ranked_floating_point); preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::vector>&& structs_ranked_columns, - bool safe_for_two_table_comparator); + bool ranked_floating_point); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -861,11 +868,12 @@ struct preprocessed_table { std::optional> _dremel_device_views; // Intermediate columns generated from transforming the child columns of lists-of-structs columns - // into integer columns using `cudf::rank()`, also need to be kept alive. + // into integer columns using `cudf::rank()`, need to be kept alive. std::vector> _structs_ranked_columns; - // A flag indicating if the preprocessed table is safe to use for two-table comparator. - bool const _safe_for_two_table_comparator; + // Flag to record if the input table was preprocessed to transform any lists-of-structs column + // having at least one floating-point child (at any nested level) using `cudf::rank`. + bool const _ranked_floating_point; }; /** @@ -1080,7 +1088,7 @@ class two_table_comparator { : d_left_table{std::move(left)}, d_right_table{std::move(right)} { CUDF_EXPECTS( - d_left_table->_safe_for_two_table_comparator && d_right_table->_safe_for_two_table_comparator, + d_left_table->_ranked_floating_point && d_right_table->_ranked_floating_point, "The pre-generated preprocessed_table(s) are not be able to use for two_table_comparator."); } diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 45a7b616ea3..05c5a5d3115 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -532,7 +532,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::vector>&& structs_ranked_columns, host_span column_order, host_span null_precedence, - bool safe_for_two_table_comparator, + bool ranked_floating_point, rmm::cuda_stream_view stream) { // auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = @@ -558,7 +558,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(dremel_data), std::move(d_dremel_device_view), std::move(structs_ranked_columns), - safe_for_two_table_comparator)); + ranked_floating_point)); } else { return std::shared_ptr( new preprocessed_table(std::move(d_t), @@ -566,7 +566,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(d_null_precedence), std::move(d_depths), std::move(structs_ranked_columns), - safe_for_two_table_comparator)); + ranked_floating_point)); } } @@ -591,7 +591,7 @@ std::shared_ptr preprocessed_table::create( return col.type().id() == type_id::FLOAT32 || col.type().id() == type_id::FLOAT64 || std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); }; - auto const safe_for_two_table_comparator = + auto const ranked_floating_point = structs_ranked_columns.size() == 0 || std::none_of(input.begin(), input.end(), has_nested_floating_point); @@ -600,7 +600,7 @@ std::shared_ptr preprocessed_table::create( std::move(structs_ranked_columns), new_column_order, new_null_precedence, - safe_for_two_table_comparator, + ranked_floating_point, stream); } @@ -642,14 +642,14 @@ preprocessed_table::create(table_view const& lhs, std::move(structs_ranked_columns_lhs), new_column_order_lhs, new_null_precedence_lhs, - true /*safe_for_two_table_comparator*/, + true /*ranked_floating_point*/, stream), create_preprocessed_table(transformed_rhs_opt.value(), std::move(verticalized_col_depths_rhs), std::move(structs_ranked_columns_rhs), new_column_order_lhs, new_null_precedence_lhs, - true /*safe_for_two_table_comparator*/, + true /*ranked_floating_point*/, stream)}; } @@ -661,7 +661,7 @@ preprocessed_table::preprocessed_table( std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::vector>&& structs_ranked_columns, - bool safe_for_two_table_comparator) + bool ranked_floating_point) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -669,7 +669,7 @@ preprocessed_table::preprocessed_table( _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), _structs_ranked_columns(std::move(structs_ranked_columns)), - _safe_for_two_table_comparator(safe_for_two_table_comparator) + _ranked_floating_point(ranked_floating_point) { } @@ -679,7 +679,7 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::vector>&& structs_ranked_columns, - bool safe_for_two_table_comparator) + bool ranked_floating_point) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -687,7 +687,7 @@ preprocessed_table::preprocessed_table( _dremel_data{}, _dremel_device_views{}, _structs_ranked_columns(std::move(structs_ranked_columns)), - _safe_for_two_table_comparator(safe_for_two_table_comparator) + _ranked_floating_point(ranked_floating_point) { } From cdcc25a1b705096c16c7326d5a9c55c9998073e3 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 11 Apr 2023 14:54:15 -0700 Subject: [PATCH 096/140] Change error message --- .../cudf/table/experimental/row_operators.cuh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 09207af2114..993189336af 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -955,9 +955,9 @@ class self_comparator { auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS( - d_t->_structs_ranked_columns.size() == 0, - "The input table was preprocessed using a different type of physical element comparator."); + CUDF_EXPECTS(!d_t->_ranked_floating_point, + "The input table has floating-point number and was preprocessed using a " + "different type of physical element comparator."); } return less_comparator{ @@ -980,9 +980,9 @@ class self_comparator { auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS( - d_t->_structs_ranked_columns.size() == 0, - "The input table was preprocessed using a different type of physical element comparator."); + CUDF_EXPECTS(!d_t->_ranked_floating_point, + "The input table has floating-point number and was preprocessed using a " + "different type of physical element comparator."); } return less_equivalent_comparator{ From 77f98c54e9508da86d51d8e0ce3dd4aa9f19e912 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 11 Apr 2023 15:16:59 -0700 Subject: [PATCH 097/140] Fix docs for `decompose_structs` --- cpp/src/table/row_operators.cu | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 05c5a5d3115..cfd03ae190d 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -14,8 +14,6 @@ * limitations under the License. */ -#include - #include #include #include @@ -138,12 +136,13 @@ table_view remove_struct_child_offsets(table_view table) * | | * i f * - * When list columns are present, the decomposition is performed similarly to pure structs but list - * parent columns are NOT pruned - * - * // TODO: stop with list. + * In the case of structs column with a lists column as its first child such as + * `Struct, float>`, after decomposition we get three columns `Struct<>`, + * `List`, and `float`. * - * For example, if the original table has a column `List>`, + * When list columns are present, depending on the input flag `decompose_lists`, the decomposition + * can be performed similarly to pure structs but list parent columns are NOT pruned. For example, + * if the original table has a column `List>`, * * L * | @@ -162,20 +161,17 @@ table_view remove_struct_child_offsets(table_view table) * The list parents are still needed to define the range of elements in the leaf that belong to the * same row. * - * In the case of structs column with a lists column as its first child such as - * `Struct, float>`, after decomposition we get three columns `Struct<>`, - * `List`, and `float`. - * * @param table The table whose struct columns to decompose. + * @param decompose_lists Whether to decompose lists columns or output them unchanged * @param column_order The per-column order if using output with lexicographic comparison * @param null_precedence The per-column null precedence * @return A tuple containing a table with all struct columns decomposed, new corresponding column * orders and null precedences and depths of the linearized branches */ auto decompose_structs(table_view table, + bool decompose_lists, host_span column_order = {}, - host_span null_precedence = {}, - bool decompose_lists = false) + host_span null_precedence = {}) { auto linked_columns = detail::table_to_linked_columns(table); @@ -329,6 +325,8 @@ void check_lex_compatibility(table_view const& input) check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { + CUDF_EXPECTS(child->type().id() != type_id::LIST, + "Cannot lexicographic compare a table with a STRUCT of LIST column"); check_column(*child); } } @@ -577,7 +575,7 @@ std::shared_ptr preprocessed_table::create( rmm::cuda_stream_view stream) { auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = - decompose_structs(input, column_order, null_precedence); + decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); // Next, transform any (nested) lists-of-structs column into lists-of-integers column. [[maybe_unused]] auto [transformed_t, unused_0, structs_ranked_columns, unused_1] = @@ -614,9 +612,10 @@ preprocessed_table::create(table_view const& lhs, auto [decomposed_lhs, new_column_order_lhs, new_null_precedence_lhs, - verticalized_col_depths_lhs] = decompose_structs(lhs, column_order, null_precedence); + verticalized_col_depths_lhs] = + decompose_structs(lhs, false /*no decompose lists*/, column_order, null_precedence); [[maybe_unused]] auto [decomposed_rhs, unused0, unused1, verticalized_col_depths_rhs] = - decompose_structs(rhs, column_order, null_precedence); + decompose_structs(rhs, false /*no decompose lists*/, column_order, null_precedence); // printf("line %d\n", __LINE__); // cudf::test::print(decomposed_lhs.column(0)); @@ -714,7 +713,8 @@ std::shared_ptr preprocessed_table::create(table_view const& auto [null_pushed_table, nullable_data] = structs::detail::push_down_nulls(t, stream, rmm::mr::get_current_device_resource()); auto struct_offset_removed_table = remove_struct_child_offsets(null_pushed_table); - auto verticalized_t = std::get<0>(decompose_structs(struct_offset_removed_table, {}, {}, true)); + auto verticalized_t = + std::get<0>(decompose_structs(struct_offset_removed_table, true /*decompose lists*/)); auto d_t = table_device_view_owner(table_device_view::create(verticalized_t, stream)); return std::shared_ptr(new preprocessed_table( From ecaa6c51a7ce7e7839cb6287290854a8d02c67c2 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 11 Apr 2023 16:15:17 -0700 Subject: [PATCH 098/140] Rewrite check conditions --- .../cudf/table/experimental/row_operators.cuh | 8 ++-- cpp/src/table/row_operators.cu | 46 +++++++++++++------ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 993189336af..1dc8f7278a2 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -956,7 +956,7 @@ class self_comparator { { if constexpr (!std::is_same_v) { CUDF_EXPECTS(!d_t->_ranked_floating_point, - "The input table has floating-point number and was preprocessed using a " + "The input table has floating-point numbers and was preprocessed using a " "different type of physical element comparator."); } @@ -981,7 +981,7 @@ class self_comparator { { if constexpr (!std::is_same_v) { CUDF_EXPECTS(!d_t->_ranked_floating_point, - "The input table has floating-point number and was preprocessed using a " + "The input table has floating-point numbers and was preprocessed using a " "different type of physical element comparator."); } @@ -1088,7 +1088,9 @@ class two_table_comparator { : d_left_table{std::move(left)}, d_right_table{std::move(right)} { CUDF_EXPECTS( - d_left_table->_ranked_floating_point && d_right_table->_ranked_floating_point, + d_left_table->_ranked_floating_point == d_right_table->_ranked_floating_point || + (!d_left_table->_ranked_floating_point && d_left_table->_t->num_rows() == 0) || + (!d_right_table->_ranked_floating_point && d_right_table->_t->num_rows() == 0), "The pre-generated preprocessed_table(s) are not be able to use for two_table_comparator."); } diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index cfd03ae190d..1c787f2ef2f 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -414,6 +414,8 @@ std:: auto const default_mr = rmm::mr::get_current_device_resource(); + // TODO: replace the entire input if its offsets is not zero + if (lhs.type().id() == type_id::LIST) { // Should not use sliced child because we reuse the input's offset value and offsets child. auto const child_lhs = lhs.child(lists_column_view::child_column_index); @@ -502,6 +504,11 @@ transform_lists_of_structs(table_view const& lhs, std::optional const& rhs, rmm::cuda_stream_view stream) { + if (lhs.num_rows() == 0) { + return { + lhs, rhs, std::vector>{}, std::vector>{}}; + } + std::vector transformed_lhs_cols; std::vector transformed_rhs_cols; std::vector> ranks_cols_lhs; @@ -522,6 +529,20 @@ transform_lists_of_structs(table_view const& lhs, std::move(ranks_cols_rhs)}; } +bool has_floating_point_in_struct(table_view const& input) +{ + std::function const has_nested_floating_point = [&](auto const& col) { + return col.type().id() == type_id::FLOAT32 || col.type().id() == type_id::FLOAT64 || + std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); + }; + + return std::any_of(input.begin(), input.end(), [&](auto const& col) { + return col.type().id() == type_id::STRUCT + ? std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point) + : false; + }); +} + } // namespace std::shared_ptr preprocessed_table::create_preprocessed_table( @@ -577,21 +598,10 @@ std::shared_ptr preprocessed_table::create( auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); - // Next, transform any (nested) lists-of-structs column into lists-of-integers column. [[maybe_unused]] auto [transformed_t, unused_0, structs_ranked_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, stream); - - // Since the input table is preprocessed alone, it is safe to use for comparing against other - // table in two-table comparator only if: - // - Not any transformation for lists-of-structs was performed, or. - // - The input column does not have any floating-point column at any nested level. - std::function const has_nested_floating_point = [&](auto const& col) { - return col.type().id() == type_id::FLOAT32 || col.type().id() == type_id::FLOAT64 || - std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); - }; auto const ranked_floating_point = - structs_ranked_columns.size() == 0 || - std::none_of(input.begin(), input.end(), has_nested_floating_point); + structs_ranked_columns.size() > 0 && has_floating_point_in_struct(input); return create_preprocessed_table(transformed_t, std::move(verticalized_col_depths), @@ -609,6 +619,8 @@ preprocessed_table::create(table_view const& lhs, host_span null_precedence, rmm::cuda_stream_view stream) { + check_shape_compatibility(lhs, rhs); + auto [decomposed_lhs, new_column_order_lhs, new_null_precedence_lhs, @@ -630,6 +642,11 @@ preprocessed_table::create(table_view const& lhs, structs_ranked_columns_rhs] = transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); + auto const ranked_floating_point_lhs = + structs_ranked_columns_lhs.size() > 0 && has_floating_point_in_struct(lhs); + auto const ranked_floating_point_rhs = + structs_ranked_columns_rhs.size() > 0 && has_floating_point_in_struct(rhs); + // printf("line %d\n", __LINE__); // cudf::test::print(transformed_lhs.column(0)); @@ -641,14 +658,14 @@ preprocessed_table::create(table_view const& lhs, std::move(structs_ranked_columns_lhs), new_column_order_lhs, new_null_precedence_lhs, - true /*ranked_floating_point*/, + ranked_floating_point_lhs, stream), create_preprocessed_table(transformed_rhs_opt.value(), std::move(verticalized_col_depths_rhs), std::move(structs_ranked_columns_rhs), new_column_order_lhs, new_null_precedence_lhs, - true /*ranked_floating_point*/, + ranked_floating_point_rhs, stream)}; } @@ -696,7 +713,6 @@ two_table_comparator::two_table_comparator(table_view const& left, host_span null_precedence, rmm::cuda_stream_view stream) { - check_shape_compatibility(left, right); std::tie(d_left_table, d_right_table) = preprocessed_table::create(left, right, column_order, null_precedence, stream); } From 5a77828faf26ad1950a93133224623536c49e75c Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 11 Apr 2023 19:49:56 -0700 Subject: [PATCH 099/140] Add sliced input test --- cpp/tests/sort/sort_nested_types_tests.cpp | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index 2e8d3e77905..576dce4bd2a 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -305,6 +305,38 @@ TEST_F(NestedListTest, SimpleListsOfStructsNoNulls) } } +TEST_F(NestedListTest, SlicedListsOfStructsNoNulls) +{ + auto constexpr dont_care{0}; + auto const input_original = [] { + auto const get_structs = [] { + auto child0 = + int32s_col{dont_care, dont_care, 3, 2, 3, 3, 4, 2, 4, 4, 1, 0, 3, 0, 2, 5, 4, dont_care}; + auto child1 = + int32s_col{dont_care, dont_care, 0, 4, 3, 2, 1, 1, 5, 1, 5, 5, 4, 2, 4, 1, 3, dont_care}; + return structs_col{{child0, child1}}; + }; + return cudf::make_lists_column(11, + int32s_col{0, 1, 2, 5, 7, 8, 8, 10, 12, 14, 17, 18}.release(), + get_structs().release(), + 0, + {}); + }(); + auto const input = cudf::slice(*input_original, {2, 10})[0]; + + { + auto const expected_order = int32s_col{3, 5, 2, 7, 0, 1, 6, 4}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{4, 6, 1, 0, 7, 2, 5, 3}; + auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } +} + TEST_F(NestedListTest, ListsOfEqualStructsNoNulls) { auto const input = [] { From 787a6b683b9114efed0c65ea47a98e8f8ba8ff21 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 11 Apr 2023 19:50:22 -0700 Subject: [PATCH 100/140] Implement handling for sliced input --- cpp/src/table/row_operators.cu | 153 ++++++++++++++++++++++----------- 1 file changed, 105 insertions(+), 48 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 1c787f2ef2f..e72373492d1 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -14,6 +14,10 @@ * limitations under the License. */ +#include + +#include + #include #include #include @@ -393,35 +397,50 @@ namespace { * @param lhs The input lhs column to transform * @param rhs The input rhs column to transform (if available) * @param stream CUDA stream used for device memory operations and kernel launches + * + * // TODO * @return A pair of new column_view representing the transformed input and the generated rank * (size_type) column(s) which need to be kept alive */ -std:: - tuple, std::unique_ptr, std::unique_ptr> - transform_lists_of_structs(column_view const& lhs, - std::optional const& rhs, - rmm::cuda_stream_view stream) +std::tuple, + std::vector>, + std::vector>> +transform_lists_of_structs(column_view const& lhs, + std::optional const& rhs, + rmm::cuda_stream_view stream) { - auto const make_transformed_input = [&](auto const& input, auto const& new_child) { - return column_view{data_type{type_id::LIST}, - input.size(), - nullptr, - input.null_mask(), - input.null_count(), - input.offset(), - {input.child(lists_column_view::offsets_column_index), new_child}}; - }; - auto const default_mr = rmm::mr::get_current_device_resource(); - // TODO: replace the entire input if its offsets is not zero + auto const make_transformed_input = + [&](auto const& input, + auto const& new_child) -> std::pair> { + auto new_offsets = input.offset() == 0 ? nullptr + : cudf::lists::detail::get_normalized_offsets( + lists_column_view{input}, stream, default_mr); + auto const offsets_cv = input.offset() == 0 + ? input.child(lists_column_view::offsets_column_index) + : new_offsets->view(); + return std::pair{column_view{data_type{type_id::LIST}, + input.size(), + nullptr, + input.null_mask(), + input.null_count(), + 0, + {offsets_cv, new_child}}, + std::move(new_offsets)}; + }; + + std::vector> out_cols_lhs; + std::vector> out_cols_rhs; if (lhs.type().id() == type_id::LIST) { // Should not use sliced child because we reuse the input's offset value and offsets child. - auto const child_lhs = lhs.child(lists_column_view::child_column_index); + auto const child_lhs = cudf::lists_column_view{lhs}.get_sliced_child(stream); auto const child_rhs = - rhs ? std::optional{rhs.value().child(lists_column_view::child_column_index)} - : std::nullopt; + rhs + ? std::optional{cudf::lists_column_view{rhs.value()}.get_sliced_child(stream)} + : std::nullopt; if (child_lhs.type().id() == type_id::STRUCT) { if (rhs) { @@ -439,12 +458,18 @@ std:: ranks->view(), {0, child_lhs.size(), child_lhs.size(), child_lhs.size() + child_rhs.value().size()}, stream); - auto child_lhs_ranks = std::make_unique(ranks_slices.front()); - auto child_rhs_ranks = std::make_unique(ranks_slices.back()); - auto transformed_lhs = make_transformed_input(lhs, child_lhs_ranks->view()); - auto transformed_rhs = make_transformed_input(rhs.value(), child_rhs_ranks->view()); - return { - transformed_lhs, transformed_rhs, std::move(child_lhs_ranks), std::move(child_rhs_ranks)}; + + out_cols_lhs.emplace_back(std::make_unique(ranks_slices.front())); + auto [transformed_lhs, new_offsets_lhs] = + make_transformed_input(lhs, out_cols_lhs.back()->view()); + if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } + + out_cols_rhs.emplace_back(std::make_unique(ranks_slices.back())); + auto [transformed_rhs, new_offsets_rhs] = + make_transformed_input(rhs.value(), out_cols_rhs.back()->view()); + if (new_offsets_rhs) { out_cols_rhs.emplace_back(std::move(new_offsets_rhs)); } + + return {transformed_lhs, transformed_rhs, std::move(out_cols_lhs), std::move(out_cols_rhs)}; } else { // Dense ranks should be used instead of first rank. // Consider this example: input = [ [{0, "a"}, {3, "c"}], [{0, "a"}, {2, "b"}] ]. @@ -459,19 +484,44 @@ std:: false /*percentage*/, stream, default_mr); - auto transformed_lhs = make_transformed_input(lhs, child_lhs_ranks->view()); - return {std::move(transformed_lhs), std::nullopt, std::move(child_lhs_ranks), nullptr}; + printf("line %d\n", __LINE__); + cudf::test::print(child_lhs); + + out_cols_lhs.emplace_back(std::move(child_lhs_ranks)); + auto [transformed_lhs, new_offsets_lhs] = + make_transformed_input(lhs, out_cols_lhs.back()->view()); + if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } + + return {std::move(transformed_lhs), + std::nullopt, + std::move(out_cols_lhs), + std::move(out_cols_rhs)}; } } else if (child_lhs.type().id() == type_id::LIST) { - auto [new_child_lhs, new_child_rhs_opt, child_lhs_ranks, child_rhs_ranks] = + auto [new_child_lhs, new_child_rhs_opt, out_cols_child_lhs, out_cols_child_rhs] = transform_lists_of_structs(child_lhs, child_rhs, stream); - if (child_lhs_ranks || child_rhs_ranks) { - auto transformed_lhs = make_transformed_input(lhs, new_child_lhs); - auto transformed_rhs = rhs ? std::optional{make_transformed_input( - rhs.value(), new_child_rhs_opt.value())} - : std::nullopt; - return { - transformed_lhs, transformed_rhs, std::move(child_lhs_ranks), std::move(child_rhs_ranks)}; + out_cols_lhs.insert(out_cols_lhs.end(), + std::make_move_iterator(out_cols_child_lhs.begin()), + std::make_move_iterator(out_cols_child_lhs.end())); + out_cols_rhs.insert(out_cols_rhs.end(), + std::make_move_iterator(out_cols_child_rhs.begin()), + std::make_move_iterator(out_cols_child_rhs.end())); + + if (out_cols_child_lhs.size() > 0 || out_cols_child_rhs.size() > 0) { + auto [transformed_lhs, new_offsets_lhs] = make_transformed_input(lhs, new_child_lhs); + if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } + if (!rhs) { + return {transformed_lhs, std::nullopt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; + } else { + auto [transformed_rhs, new_offsets_rhs] = + make_transformed_input(rhs.value(), new_child_rhs_opt.value()); + if (new_offsets_rhs) { out_cols_rhs.emplace_back(std::move(new_offsets_rhs)); } + + return {transformed_lhs, + std::optional{transformed_rhs}, + std::move(out_cols_lhs), + std::move(out_cols_rhs)}; + } } } } else if (lhs.type().id() == type_id::STRUCT) { @@ -481,7 +531,7 @@ std:: "Structs columns should be decomposed before reaching this function."); } - return {lhs, rhs, nullptr, nullptr}; + return {lhs, rhs, std::move(out_cols_lhs), std::move(out_cols_rhs)}; } /** @@ -509,22 +559,29 @@ transform_lists_of_structs(table_view const& lhs, lhs, rhs, std::vector>{}, std::vector>{}}; } - std::vector transformed_lhs_cols; - std::vector transformed_rhs_cols; + std::vector transformed_lhs_cvs; + std::vector transformed_rhs_cvs; std::vector> ranks_cols_lhs; std::vector> ranks_cols_rhs; - for (size_type child_idx = 0; child_idx < lhs.num_columns(); ++child_idx) { - auto const& col = lhs.column(child_idx); - auto [transformed_lhs, transformed_rhs_opt, ranks_lhs, ranks_rhs] = transform_lists_of_structs( - col, rhs ? std::optional{rhs.value().column(child_idx)} : std::nullopt, stream); - transformed_lhs_cols.push_back(transformed_lhs); - if (rhs) { transformed_rhs_cols.push_back(transformed_rhs_opt.value()); } - if (ranks_lhs) { ranks_cols_lhs.emplace_back(std::move(ranks_lhs)); } - if (ranks_rhs) { ranks_cols_rhs.emplace_back(std::move(ranks_rhs)); } + for (size_type col_idx = 0; col_idx < lhs.num_columns(); ++col_idx) { + auto const& lhs_col = lhs.column(col_idx); + auto const rhs_col_opt = + rhs ? std::optional{rhs.value().column(col_idx)} : std::nullopt; + + auto [transformed_lhs, transformed_rhs_opt, out_cols_lhs, out_cols_rhs] = + transform_lists_of_structs(lhs_col, rhs_col_opt, stream); + transformed_lhs_cvs.push_back(transformed_lhs); + if (rhs) { transformed_rhs_cvs.push_back(transformed_rhs_opt.value()); } + ranks_cols_lhs.insert(ranks_cols_lhs.end(), + std::make_move_iterator(out_cols_lhs.begin()), + std::make_move_iterator(out_cols_lhs.end())); + ranks_cols_rhs.insert(ranks_cols_rhs.end(), + std::make_move_iterator(out_cols_rhs.begin()), + std::make_move_iterator(out_cols_rhs.end())); } - return {table_view{transformed_lhs_cols}, - rhs ? std::optional{table_view{transformed_rhs_cols}} : std::nullopt, + return {table_view{transformed_lhs_cvs}, + rhs ? std::optional{table_view{transformed_rhs_cvs}} : std::nullopt, std::move(ranks_cols_lhs), std::move(ranks_cols_rhs)}; } From 0d7fcaf7cae1f81eaded492a12d4344f2b04638c Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 12 Apr 2023 20:40:09 -0700 Subject: [PATCH 101/140] Rename variables and rewrite docs --- .../cudf/table/experimental/row_operators.cuh | 2 +- cpp/src/table/row_operators.cu | 100 +++++++++--------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 1dc8f7278a2..f76edaa2596 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -869,7 +869,7 @@ struct preprocessed_table { // Intermediate columns generated from transforming the child columns of lists-of-structs columns // into integer columns using `cudf::rank()`, need to be kept alive. - std::vector> _structs_ranked_columns; + std::vector> _structs_transformed_columns; // Flag to record if the input table was preprocessed to transform any lists-of-structs column // having at least one floating-point child (at any nested level) using `cudf::rank`. diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index e72373492d1..a25e22d4001 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -16,8 +16,6 @@ #include -#include - #include #include #include @@ -194,7 +192,7 @@ auto decompose_structs(table_view table, std::vector* branch, int depth) { branch->push_back(c); - if (c->type().id() == type_id::LIST && decompose_lists) { + if (decompose_lists && c->type().id() == type_id::LIST) { recursive_child( c->children[lists_column_view::child_column_index].get(), branch, depth + 1); } else if (c->type().id() == type_id::STRUCT) { @@ -389,7 +387,7 @@ namespace { * @brief Transform any nested lists-of-structs column into lists-of-integers column. * * For a lists-of-structs column at any nested level, its child structs column will be replaced by a - * size_type column generated using `cudf::rank()`. + * size_type column computed as its ranks. * * If the input column is not lists-of-structs, or does not contain lists-of-structs at any nested * level, the input will be passed through without any changes. @@ -398,21 +396,25 @@ namespace { * @param rhs The input rhs column to transform (if available) * @param stream CUDA stream used for device memory operations and kernel launches * - * // TODO - * @return A pair of new column_view representing the transformed input and the generated rank - * (size_type) column(s) which need to be kept alive + * @return A tuple consisting of new column_view representing the transformed input, along with + * their generated ranks (size_type column(s)) and possibly new list offsets */ std::tuple, std::vector>, std::vector>> transform_lists_of_structs(column_view const& lhs, - std::optional const& rhs, + std::optional const& rhs_opt, rmm::cuda_stream_view stream) { auto const default_mr = rmm::mr::get_current_device_resource(); - auto const make_transformed_input = + // If the input is not sliced, just replace the input child by new_child. + // Otherwise, we have to generate new offsets and replace both offsets/child of the input by the + // new ones. This is because the new child here is generated by ranking and always has zero + // offset thus cannot replace the input child if it is sliced. + // The new offsets column needs to be returned and kept alive. + auto const replace_child = [&](auto const& input, auto const& new_child) -> std::pair> { auto new_offsets = input.offset() == 0 ? nullptr @@ -435,17 +437,13 @@ transform_lists_of_structs(column_view const& lhs, std::vector> out_cols_rhs; if (lhs.type().id() == type_id::LIST) { - // Should not use sliced child because we reuse the input's offset value and offsets child. auto const child_lhs = cudf::lists_column_view{lhs}.get_sliced_child(stream); - auto const child_rhs = - rhs - ? std::optional{cudf::lists_column_view{rhs.value()}.get_sliced_child(stream)} - : std::nullopt; if (child_lhs.type().id() == type_id::STRUCT) { - if (rhs) { - auto child_lhs_rhs = cudf::detail::concatenate( - std::vector{child_lhs, child_rhs.value()}, stream, default_mr); + if (rhs_opt) { + auto const child_rhs = cudf::lists_column_view{rhs_opt.value()}.get_sliced_child(stream); + auto child_lhs_rhs = cudf::detail::concatenate( + std::vector{child_lhs, child_rhs}, stream, default_mr); auto const ranks = cudf::detail::rank(child_lhs_rhs->view(), rank_method::DENSE, order::ASCENDING, @@ -456,17 +454,16 @@ transform_lists_of_structs(column_view const& lhs, default_mr); auto const ranks_slices = cudf::detail::slice( ranks->view(), - {0, child_lhs.size(), child_lhs.size(), child_lhs.size() + child_rhs.value().size()}, + {0, child_lhs.size(), child_lhs.size(), child_lhs.size() + child_rhs.size()}, stream); out_cols_lhs.emplace_back(std::make_unique(ranks_slices.front())); - auto [transformed_lhs, new_offsets_lhs] = - make_transformed_input(lhs, out_cols_lhs.back()->view()); + auto [transformed_lhs, new_offsets_lhs] = replace_child(lhs, out_cols_lhs.back()->view()); if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } out_cols_rhs.emplace_back(std::make_unique(ranks_slices.back())); auto [transformed_rhs, new_offsets_rhs] = - make_transformed_input(rhs.value(), out_cols_rhs.back()->view()); + replace_child(rhs_opt.value(), out_cols_rhs.back()->view()); if (new_offsets_rhs) { out_cols_rhs.emplace_back(std::move(new_offsets_rhs)); } return {transformed_lhs, transformed_rhs, std::move(out_cols_lhs), std::move(out_cols_rhs)}; @@ -474,8 +471,8 @@ transform_lists_of_structs(column_view const& lhs, // Dense ranks should be used instead of first rank. // Consider this example: input = [ [{0, "a"}, {3, "c"}], [{0, "a"}, {2, "b"}] ]. // If first rank is used, transformed_input = [ [0, 3], [1, 2] ]. Comparing them will lead - // to the result row(0) < row(1) which is incorrect. With dense rank, transformed_input = [ - // [0, 2], [0, 1] ], producing correct comparison. + // to the result row(0) < row(1) which is incorrect. + // With dense rank, transformed_input = [ [0, 2], [0, 1] ], producing correct comparison. auto child_lhs_ranks = cudf::detail::rank(child_lhs, rank_method::DENSE, order::ASCENDING, @@ -484,12 +481,9 @@ transform_lists_of_structs(column_view const& lhs, false /*percentage*/, stream, default_mr); - printf("line %d\n", __LINE__); - cudf::test::print(child_lhs); out_cols_lhs.emplace_back(std::move(child_lhs_ranks)); - auto [transformed_lhs, new_offsets_lhs] = - make_transformed_input(lhs, out_cols_lhs.back()->view()); + auto [transformed_lhs, new_offsets_lhs] = replace_child(lhs, out_cols_lhs.back()->view()); if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } return {std::move(transformed_lhs), @@ -498,8 +492,14 @@ transform_lists_of_structs(column_view const& lhs, std::move(out_cols_rhs)}; } } else if (child_lhs.type().id() == type_id::LIST) { + auto const child_rhs_opt = + rhs_opt + ? std::optional{cudf::lists_column_view{rhs_opt.value()}.get_sliced_child( + stream)} + : std::nullopt; + auto [new_child_lhs, new_child_rhs_opt, out_cols_child_lhs, out_cols_child_rhs] = - transform_lists_of_structs(child_lhs, child_rhs, stream); + transform_lists_of_structs(child_lhs, child_rhs_opt, stream); out_cols_lhs.insert(out_cols_lhs.end(), std::make_move_iterator(out_cols_child_lhs.begin()), std::make_move_iterator(out_cols_child_lhs.end())); @@ -508,13 +508,13 @@ transform_lists_of_structs(column_view const& lhs, std::make_move_iterator(out_cols_child_rhs.end())); if (out_cols_child_lhs.size() > 0 || out_cols_child_rhs.size() > 0) { - auto [transformed_lhs, new_offsets_lhs] = make_transformed_input(lhs, new_child_lhs); + auto [transformed_lhs, new_offsets_lhs] = replace_child(lhs, new_child_lhs); if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } - if (!rhs) { + if (!rhs_opt) { return {transformed_lhs, std::nullopt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; } else { auto [transformed_rhs, new_offsets_rhs] = - make_transformed_input(rhs.value(), new_child_rhs_opt.value()); + replace_child(rhs_opt.value(), new_child_rhs_opt.value()); if (new_offsets_rhs) { out_cols_rhs.emplace_back(std::move(new_offsets_rhs)); } return {transformed_lhs, @@ -524,14 +524,18 @@ transform_lists_of_structs(column_view const& lhs, } } } + // else == child is not STRUCT or LIST: just go to the end of this function. } else if (lhs.type().id() == type_id::STRUCT) { + // Any structs-of-lists should be decomposed into empty struct type `Struct<>` before being + // processed by this function. CUDF_EXPECTS(std::all_of(lhs.child_begin(), lhs.child_end(), [](auto const& child) { return child.type().id() != type_id::LIST; }), "Structs columns should be decomposed before reaching this function."); } - return {lhs, rhs, std::move(out_cols_lhs), std::move(out_cols_rhs)}; + // Passthrough: nothing changed. + return {lhs, rhs_opt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; } /** @@ -605,7 +609,7 @@ bool has_floating_point_in_struct(table_view const& input) std::shared_ptr preprocessed_table::create_preprocessed_table( table_view const& preprocessed_input, std::vector&& verticalized_col_depths, - std::vector>&& structs_ranked_columns, + std::vector>&& structs_transformed_columns, host_span column_order, host_span null_precedence, bool ranked_floating_point, @@ -633,7 +637,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(d_depths), std::move(dremel_data), std::move(d_dremel_device_view), - std::move(structs_ranked_columns), + std::move(structs_transformed_columns), ranked_floating_point)); } else { return std::shared_ptr( @@ -641,7 +645,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), - std::move(structs_ranked_columns), + std::move(structs_transformed_columns), ranked_floating_point)); } } @@ -655,14 +659,14 @@ std::shared_ptr preprocessed_table::create( auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); - [[maybe_unused]] auto [transformed_t, unused_0, structs_ranked_columns, unused_1] = + [[maybe_unused]] auto [transformed_t, unused_0, structs_transformed_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, stream); auto const ranked_floating_point = - structs_ranked_columns.size() > 0 && has_floating_point_in_struct(input); + structs_transformed_columns.size() > 0 && has_floating_point_in_struct(input); return create_preprocessed_table(transformed_t, std::move(verticalized_col_depths), - std::move(structs_ranked_columns), + std::move(structs_transformed_columns), new_column_order, new_null_precedence, ranked_floating_point, @@ -695,14 +699,14 @@ preprocessed_table::create(table_view const& lhs, // Transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_lhs, transformed_rhs_opt, - structs_ranked_columns_lhs, - structs_ranked_columns_rhs] = + structs_transformed_columns_lhs, + structs_transformed_columns_rhs] = transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); auto const ranked_floating_point_lhs = - structs_ranked_columns_lhs.size() > 0 && has_floating_point_in_struct(lhs); + structs_transformed_columns_lhs.size() > 0 && has_floating_point_in_struct(lhs); auto const ranked_floating_point_rhs = - structs_ranked_columns_rhs.size() > 0 && has_floating_point_in_struct(rhs); + structs_transformed_columns_rhs.size() > 0 && has_floating_point_in_struct(rhs); // printf("line %d\n", __LINE__); // cudf::test::print(transformed_lhs.column(0)); @@ -712,14 +716,14 @@ preprocessed_table::create(table_view const& lhs, return {create_preprocessed_table(transformed_lhs, std::move(verticalized_col_depths_lhs), - std::move(structs_ranked_columns_lhs), + std::move(structs_transformed_columns_lhs), new_column_order_lhs, new_null_precedence_lhs, ranked_floating_point_lhs, stream), create_preprocessed_table(transformed_rhs_opt.value(), std::move(verticalized_col_depths_rhs), - std::move(structs_ranked_columns_rhs), + std::move(structs_transformed_columns_rhs), new_column_order_lhs, new_null_precedence_lhs, ranked_floating_point_rhs, @@ -733,7 +737,7 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::vector>&& structs_ranked_columns, + std::vector>&& structs_transformed_columns, bool ranked_floating_point) : _t(std::move(table)), _column_order(std::move(column_order)), @@ -741,7 +745,7 @@ preprocessed_table::preprocessed_table( _depths(std::move(depths)), _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), - _structs_ranked_columns(std::move(structs_ranked_columns)), + _structs_transformed_columns(std::move(structs_transformed_columns)), _ranked_floating_point(ranked_floating_point) { } @@ -751,7 +755,7 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::vector>&& structs_ranked_columns, + std::vector>&& structs_transformed_columns, bool ranked_floating_point) : _t(std::move(table)), _column_order(std::move(column_order)), @@ -759,7 +763,7 @@ preprocessed_table::preprocessed_table( _depths(std::move(depths)), _dremel_data{}, _dremel_device_views{}, - _structs_ranked_columns(std::move(structs_ranked_columns)), + _structs_transformed_columns(std::move(structs_transformed_columns)), _ranked_floating_point(ranked_floating_point) { } From 6139dc2f8cd518922e980167a249c8cd4631c84c Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 12 Apr 2023 21:34:43 -0700 Subject: [PATCH 102/140] Rewrite `transform_lists_of_structs` --- cpp/src/table/row_operators.cu | 159 ++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 73 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index a25e22d4001..34d4eb65d91 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -395,9 +395,9 @@ namespace { * @param lhs The input lhs column to transform * @param rhs The input rhs column to transform (if available) * @param stream CUDA stream used for device memory operations and kernel launches - * * @return A tuple consisting of new column_view representing the transformed input, along with - * their generated ranks (size_type column(s)) and possibly new list offsets + * their ranks (size_type column(s)) and possibly new list offsets generated during the + * transformation process */ std::tuple, @@ -414,23 +414,50 @@ transform_lists_of_structs(column_view const& lhs, // new ones. This is because the new child here is generated by ranking and always has zero // offset thus cannot replace the input child if it is sliced. // The new offsets column needs to be returned and kept alive. - auto const replace_child = - [&](auto const& input, - auto const& new_child) -> std::pair> { - auto new_offsets = input.offset() == 0 ? nullptr - : cudf::lists::detail::get_normalized_offsets( - lists_column_view{input}, stream, default_mr); - auto const offsets_cv = input.offset() == 0 - ? input.child(lists_column_view::offsets_column_index) - : new_offsets->view(); - return std::pair{column_view{data_type{type_id::LIST}, - input.size(), - nullptr, - input.null_mask(), - input.null_count(), - 0, - {offsets_cv, new_child}}, - std::move(new_offsets)}; + auto const replace_child = [&](column_view const& input, + column_view const& new_child, + std::vector>& out_cols) { + auto const make_output = [&input](auto const& offsets_cv, auto const& child_cv) { + return column_view{data_type{type_id::LIST}, + input.size(), + nullptr, + input.null_mask(), + input.null_count(), + 0, + {offsets_cv, child_cv}}; + }; + + if (input.offset() == 0) { + return make_output(input.child(lists_column_view::offsets_column_index), new_child); + } + + out_cols.emplace_back( + cudf::lists::detail::get_normalized_offsets(lists_column_view{input}, stream, default_mr)); + return make_output(out_cols.back()->view(), new_child); + }; + + // Dense ranks should be used instead of first rank. + // Consider this example: `input = [ [{0, "a"}, {3, "c"}], [{0, "a"}, {2, "b"}] ]`. + // If first rank is used, `transformed_input = [ [0, 3], [1, 2] ]`. Comparing them will lead + // to the result row(0) < row(1) which is incorrect. + // With dense rank, `transformed_input = [ [0, 2], [0, 1] ]`, producing correct comparison. + // + // In addition, since the ranked structs column(s) are nested child column instead of + // top-level column, the column order and null order should be fixed to the same values in all + // situations. + // For example, with the same input above, using the fixed values for column order + // (`order::ASCENDING`) and null order (`null_order::BEFORE`), we have + // `transformed_input = [ [0, 2], [0, 1] ]`. Sorting of `transformed_input` will produce the + // same result as sorting `input` regardless of sorting order (ASC or DESC). + auto const compute_ranks = [&](column_view const& input) { + return cudf::detail::rank(input, + rank_method::DENSE, + order::ASCENDING, + null_policy::EXCLUDE, + null_order::BEFORE, + false /*percentage*/, + stream, + default_mr); }; std::vector> out_cols_lhs; @@ -439,92 +466,78 @@ transform_lists_of_structs(column_view const& lhs, if (lhs.type().id() == type_id::LIST) { auto const child_lhs = cudf::lists_column_view{lhs}.get_sliced_child(stream); + // Found a lists-of-structs column. if (child_lhs.type().id() == type_id::STRUCT) { - if (rhs_opt) { + if (rhs_opt) { // rhs table is available auto const child_rhs = cudf::lists_column_view{rhs_opt.value()}.get_sliced_child(stream); - auto child_lhs_rhs = cudf::detail::concatenate( + auto const concatenated_children = cudf::detail::concatenate( std::vector{child_lhs, child_rhs}, stream, default_mr); - auto const ranks = cudf::detail::rank(child_lhs_rhs->view(), - rank_method::DENSE, - order::ASCENDING, - null_policy::EXCLUDE, - null_order::BEFORE, - false /*percentage*/, - stream, - default_mr); + + auto const ranks = compute_ranks(concatenated_children->view()); auto const ranks_slices = cudf::detail::slice( ranks->view(), {0, child_lhs.size(), child_lhs.size(), child_lhs.size() + child_rhs.size()}, stream); out_cols_lhs.emplace_back(std::make_unique(ranks_slices.front())); - auto [transformed_lhs, new_offsets_lhs] = replace_child(lhs, out_cols_lhs.back()->view()); - if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } - out_cols_rhs.emplace_back(std::make_unique(ranks_slices.back())); - auto [transformed_rhs, new_offsets_rhs] = - replace_child(rhs_opt.value(), out_cols_rhs.back()->view()); - if (new_offsets_rhs) { out_cols_rhs.emplace_back(std::move(new_offsets_rhs)); } - - return {transformed_lhs, transformed_rhs, std::move(out_cols_lhs), std::move(out_cols_rhs)}; - } else { - // Dense ranks should be used instead of first rank. - // Consider this example: input = [ [{0, "a"}, {3, "c"}], [{0, "a"}, {2, "b"}] ]. - // If first rank is used, transformed_input = [ [0, 3], [1, 2] ]. Comparing them will lead - // to the result row(0) < row(1) which is incorrect. - // With dense rank, transformed_input = [ [0, 2], [0, 1] ], producing correct comparison. - auto child_lhs_ranks = cudf::detail::rank(child_lhs, - rank_method::DENSE, - order::ASCENDING, - null_policy::EXCLUDE, - null_order::BEFORE, - false /*percentage*/, - stream, - default_mr); - - out_cols_lhs.emplace_back(std::move(child_lhs_ranks)); - auto [transformed_lhs, new_offsets_lhs] = replace_child(lhs, out_cols_lhs.back()->view()); - if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } + + auto transformed_lhs = replace_child(lhs, out_cols_lhs.back()->view(), out_cols_lhs); + auto transformed_rhs = + replace_child(rhs_opt.value(), out_cols_rhs.back()->view(), out_cols_rhs); + + return {std::move(transformed_lhs), + std::optional{std::move(transformed_rhs)}, + std::move(out_cols_lhs), + std::move(out_cols_rhs)}; + } else { // rhs table is not available + out_cols_lhs.emplace_back(compute_ranks(child_lhs)); + auto transformed_lhs = replace_child(lhs, out_cols_lhs.back()->view(), out_cols_lhs); return {std::move(transformed_lhs), std::nullopt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; } - } else if (child_lhs.type().id() == type_id::LIST) { + } + // Found a lists-of-lists column. + else if (child_lhs.type().id() == type_id::LIST) { auto const child_rhs_opt = rhs_opt ? std::optional{cudf::lists_column_view{rhs_opt.value()}.get_sliced_child( stream)} : std::nullopt; + // Recursively call transformation on the child column. auto [new_child_lhs, new_child_rhs_opt, out_cols_child_lhs, out_cols_child_rhs] = transform_lists_of_structs(child_lhs, child_rhs_opt, stream); - out_cols_lhs.insert(out_cols_lhs.end(), - std::make_move_iterator(out_cols_child_lhs.begin()), - std::make_move_iterator(out_cols_child_lhs.end())); - out_cols_rhs.insert(out_cols_rhs.end(), - std::make_move_iterator(out_cols_child_rhs.begin()), - std::make_move_iterator(out_cols_child_rhs.end())); if (out_cols_child_lhs.size() > 0 || out_cols_child_rhs.size() > 0) { - auto [transformed_lhs, new_offsets_lhs] = replace_child(lhs, new_child_lhs); - if (new_offsets_lhs) { out_cols_lhs.emplace_back(std::move(new_offsets_lhs)); } - if (!rhs_opt) { - return {transformed_lhs, std::nullopt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; + out_cols_lhs.insert(out_cols_lhs.end(), + std::make_move_iterator(out_cols_child_lhs.begin()), + std::make_move_iterator(out_cols_child_lhs.end())); + out_cols_rhs.insert(out_cols_rhs.end(), + std::make_move_iterator(out_cols_child_rhs.begin()), + std::make_move_iterator(out_cols_child_rhs.end())); + + auto transformed_lhs = replace_child(lhs, new_child_lhs, out_cols_lhs); + if (rhs_opt) { + auto transformed_rhs = + replace_child(rhs_opt.value(), new_child_rhs_opt.value(), out_cols_rhs); + + return {std::move(transformed_lhs), + std::optional{std::move(transformed_rhs)}, + std::move(out_cols_lhs), + std::move(out_cols_rhs)}; } else { - auto [transformed_rhs, new_offsets_rhs] = - replace_child(rhs_opt.value(), new_child_rhs_opt.value()); - if (new_offsets_rhs) { out_cols_rhs.emplace_back(std::move(new_offsets_rhs)); } - - return {transformed_lhs, - std::optional{transformed_rhs}, + return {std::move(transformed_lhs), + std::nullopt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; } } } - // else == child is not STRUCT or LIST: just go to the end of this function. + // else == child is not STRUCT or LIST: just go to the end of this function, no transformation. } else if (lhs.type().id() == type_id::STRUCT) { // Any structs-of-lists should be decomposed into empty struct type `Struct<>` before being // processed by this function. From 4d24c2ea7eebe3dc77d779825b9fb0c861b6c887 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 12 Apr 2023 21:44:21 -0700 Subject: [PATCH 103/140] Rename variables and rewrite docs --- .../cudf/table/experimental/row_operators.cuh | 12 ++--- cpp/src/table/row_operators.cu | 46 +++++++++++-------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index f76edaa2596..43b0a39135c 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -739,7 +739,7 @@ struct preprocessed_table { * @param preprocessed_input The table resulted from preprocessing * @param verticalized_col_depths The depths of each column resulting from decomposing struct * columns in the original input table - * @param structs_ranked_columns Store the intermediate results from transforming the child + * @param structs_transformed_columns Store the intermediate results from transforming the child * columns of lists-of-structs columns into integer columns using `cudf::rank()` * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns @@ -754,7 +754,7 @@ struct preprocessed_table { static std::shared_ptr create_preprocessed_table( table_view const& preprocessed_input, std::vector&& verticalized_col_depths, - std::vector>&& structs_ranked_columns, + std::vector>&& structs_transformed_columns, host_span column_order, host_span null_precedence, bool ranked_floating_point, @@ -781,7 +781,7 @@ struct preprocessed_table { * contain an empty `dremel_device_view`. As such, this uvector has as many elements as * there are columns in the table (unlike the `dremel_data` parameter, which is only as * long as the number of list columns). - * @param structs_ranked_columns Store the intermediate results from transforming the child + * @param structs_transformed_columns Store the intermediate results from transforming the child * columns of lists-of-structs columns into integer columns using `cudf::rank()` and will * be used for row comparison. * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform @@ -793,14 +793,14 @@ struct preprocessed_table { rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::vector>&& structs_ranked_columns, + std::vector>&& structs_transformed_columns, bool ranked_floating_point); preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::vector>&& structs_ranked_columns, + std::vector>&& structs_transformed_columns, bool ranked_floating_point); /** @@ -868,7 +868,7 @@ struct preprocessed_table { std::optional> _dremel_device_views; // Intermediate columns generated from transforming the child columns of lists-of-structs columns - // into integer columns using `cudf::rank()`, need to be kept alive. + // into columns of `size_type` type using `cudf::rank()`, need to be kept alive. std::vector> _structs_transformed_columns; // Flag to record if the input table was preprocessed to transform any lists-of-structs column diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 34d4eb65d91..386f0444521 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -387,7 +387,7 @@ namespace { * @brief Transform any nested lists-of-structs column into lists-of-integers column. * * For a lists-of-structs column at any nested level, its child structs column will be replaced by a - * size_type column computed as its ranks. + * `size_type` column computed as its ranks. * * If the input column is not lists-of-structs, or does not contain lists-of-structs at any nested * level, the input will be passed through without any changes. @@ -396,8 +396,8 @@ namespace { * @param rhs The input rhs column to transform (if available) * @param stream CUDA stream used for device memory operations and kernel launches * @return A tuple consisting of new column_view representing the transformed input, along with - * their ranks (size_type column(s)) and possibly new list offsets generated during the - * transformation process + * their ranks column(s) (of `size_type` type) and possibly new list offsets generated + * during the transformation process */ std::tuple, @@ -512,6 +512,7 @@ transform_lists_of_structs(column_view const& lhs, auto [new_child_lhs, new_child_rhs_opt, out_cols_child_lhs, out_cols_child_rhs] = transform_lists_of_structs(child_lhs, child_rhs_opt, stream); + // Only transform the current pair of columns if their children have been transformed. if (out_cols_child_lhs.size() > 0 || out_cols_child_rhs.size() > 0) { out_cols_lhs.insert(out_cols_lhs.end(), std::make_move_iterator(out_cols_child_lhs.begin()), @@ -552,16 +553,18 @@ transform_lists_of_structs(column_view const& lhs, } /** - * @brief Transform any lists-of-structs column in the given table(s) into lists-of-integers column. + * @brief Transform any nested lists-of-structs column in the given table(s) into lists-of-integers + * column. * - * If the rhs table is specified, its shape should be pre-checked to match with lhs table using - * `check_shape_compatibility` before being passed into this function. + * If the rhs table is specified, its shape should be pre-checked to match with the shape of lhs + * table using `check_shape_compatibility` before being passed into this function. * * @param lhs The input lhs table to transform * @param rhs The input rhs table to transform (if available) * @param stream CUDA stream used for device memory operations and kernel launches - * @return A pair of new table_view representing the transformed input and the generated rank - * (size_type) column(s) which need to be kept alive + * @return A tuple consisting of new table_view representing the transformed input, along with + * the ranks column (of `size_type` type) and possibly new list offsets generated during the + * transformation process */ std::tuple, @@ -571,36 +574,39 @@ transform_lists_of_structs(table_view const& lhs, std::optional const& rhs, rmm::cuda_stream_view stream) { - if (lhs.num_rows() == 0) { + if (lhs.num_rows() == 0 && (!rhs || rhs.value().num_rows() == 0)) { return { lhs, rhs, std::vector>{}, std::vector>{}}; } std::vector transformed_lhs_cvs; std::vector transformed_rhs_cvs; - std::vector> ranks_cols_lhs; - std::vector> ranks_cols_rhs; + std::vector> out_cols_lhs; + std::vector> out_cols_rhs; + for (size_type col_idx = 0; col_idx < lhs.num_columns(); ++col_idx) { auto const& lhs_col = lhs.column(col_idx); auto const rhs_col_opt = rhs ? std::optional{rhs.value().column(col_idx)} : std::nullopt; - auto [transformed_lhs, transformed_rhs_opt, out_cols_lhs, out_cols_rhs] = + auto [transformed_lhs, transformed_rhs_opt, curr_out_cols_lhs, curr_out_cols_rhs] = transform_lists_of_structs(lhs_col, rhs_col_opt, stream); + transformed_lhs_cvs.push_back(transformed_lhs); if (rhs) { transformed_rhs_cvs.push_back(transformed_rhs_opt.value()); } - ranks_cols_lhs.insert(ranks_cols_lhs.end(), - std::make_move_iterator(out_cols_lhs.begin()), - std::make_move_iterator(out_cols_lhs.end())); - ranks_cols_rhs.insert(ranks_cols_rhs.end(), - std::make_move_iterator(out_cols_rhs.begin()), - std::make_move_iterator(out_cols_rhs.end())); + + out_cols_lhs.insert(out_cols_lhs.end(), + std::make_move_iterator(curr_out_cols_lhs.begin()), + std::make_move_iterator(curr_out_cols_lhs.end())); + out_cols_rhs.insert(out_cols_rhs.end(), + std::make_move_iterator(curr_out_cols_rhs.begin()), + std::make_move_iterator(curr_out_cols_rhs.end())); } return {table_view{transformed_lhs_cvs}, rhs ? std::optional{table_view{transformed_rhs_cvs}} : std::nullopt, - std::move(ranks_cols_lhs), - std::move(ranks_cols_rhs)}; + std::move(out_cols_lhs), + std::move(out_cols_rhs)}; } bool has_floating_point_in_struct(table_view const& input) From c5a0a981e5ae4363c05d98b25098a8a701c622f0 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Wed, 12 Apr 2023 21:46:44 -0700 Subject: [PATCH 104/140] Rewrite `has_floating_point_in_struct` and its docs --- cpp/src/table/row_operators.cu | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 386f0444521..38460455a33 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -609,6 +609,13 @@ transform_lists_of_structs(table_view const& lhs, std::move(out_cols_rhs)}; } +/** + * @brief Check if there is any structs column in the input table has child that is a floating-point + * column. + * + * @param input The tables to check + * @return The check result + */ bool has_floating_point_in_struct(table_view const& input) { std::function const has_nested_floating_point = [&](auto const& col) { @@ -617,9 +624,8 @@ bool has_floating_point_in_struct(table_view const& input) }; return std::any_of(input.begin(), input.end(), [&](auto const& col) { - return col.type().id() == type_id::STRUCT - ? std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point) - : false; + return col.type().id() == type_id::STRUCT && + std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); }); } From 5240f924eab8f02b14e43de33d820719e5a7d15f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 10:27:03 -0700 Subject: [PATCH 105/140] Rewrite `lists_of_structs_have_floating_point` --- cpp/src/table/row_operators.cu | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 38460455a33..f85e195ec0d 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -610,22 +610,36 @@ transform_lists_of_structs(table_view const& lhs, } /** - * @brief Check if there is any structs column in the input table has child that is a floating-point - * column. + * @brief Check if there is any lists-of-structs column in the input table that has a floating-point + * column nested inside it. * * @param input The tables to check * @return The check result */ -bool has_floating_point_in_struct(table_view const& input) +bool lists_of_structs_have_floating_point(table_view const& input) { + // Check if any (nested) column is floating-point type. std::function const has_nested_floating_point = [&](auto const& col) { return col.type().id() == type_id::FLOAT32 || col.type().id() == type_id::FLOAT64 || std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); }; return std::any_of(input.begin(), input.end(), [&](auto const& col) { - return col.type().id() == type_id::STRUCT && - std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); + // We are looking for lists-of-structs. + if (col.type().id() != type_id::LIST) { return false; } + + auto const child = col.child(lists_column_view::child_column_index); + if (child.type().id() == type_id::STRUCT && + std::any_of(child.child_begin(), child.child_end(), has_nested_floating_point)) { + return true; + } + if (child.type().id() == type_id::LIST && + lists_of_structs_have_floating_point(table_view{{child}})) { + return true; + } + + // Found a lists of some-type other than STRUCT or LIST. + return false; }); } @@ -640,9 +654,6 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl bool ranked_floating_point, rmm::cuda_stream_view stream) { - // auto [verticalized_lhs, new_column_order, new_null_precedence, verticalized_col_depths] = - // decompose_structs(input, column_order, null_precedence); - check_lex_compatibility(preprocessed_input); auto d_t = table_device_view::create(preprocessed_input, stream); @@ -687,7 +698,7 @@ std::shared_ptr preprocessed_table::create( [[maybe_unused]] auto [transformed_t, unused_0, structs_transformed_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, stream); auto const ranked_floating_point = - structs_transformed_columns.size() > 0 && has_floating_point_in_struct(input); + structs_transformed_columns.size() > 0 && lists_of_structs_have_floating_point(input); return create_preprocessed_table(transformed_t, std::move(verticalized_col_depths), @@ -729,9 +740,9 @@ preprocessed_table::create(table_view const& lhs, transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); auto const ranked_floating_point_lhs = - structs_transformed_columns_lhs.size() > 0 && has_floating_point_in_struct(lhs); + structs_transformed_columns_lhs.size() > 0 && lists_of_structs_have_floating_point(lhs); auto const ranked_floating_point_rhs = - structs_transformed_columns_rhs.size() > 0 && has_floating_point_in_struct(rhs); + structs_transformed_columns_rhs.size() > 0 && lists_of_structs_have_floating_point(rhs); // printf("line %d\n", __LINE__); // cudf::test::print(transformed_lhs.column(0)); From e9c57c7870175b3d9dfbd67e33d5c41810d4c873 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 10:36:13 -0700 Subject: [PATCH 106/140] Add check --- cpp/src/table/row_operators.cu | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index f85e195ec0d..1329a8793ee 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -539,9 +539,10 @@ transform_lists_of_structs(column_view const& lhs, } } // else == child is not STRUCT or LIST: just go to the end of this function, no transformation. - } else if (lhs.type().id() == type_id::STRUCT) { - // Any structs-of-lists should be decomposed into empty struct type `Struct<>` before being - // processed by this function. + } + // Any structs-of-lists should be decomposed into empty struct type `Struct<>` before being + // processed by this function. + else if (lhs.type().id() == type_id::STRUCT) { CUDF_EXPECTS(std::all_of(lhs.child_begin(), lhs.child_end(), [](auto const& child) { return child.type().id() != type_id::LIST; }), @@ -625,6 +626,16 @@ bool lists_of_structs_have_floating_point(table_view const& input) }; return std::any_of(input.begin(), input.end(), [&](auto const& col) { + // Any structs-of-lists should be decomposed into empty struct type `Struct<>` before being + // processed by this function. + if (col.type().id() == type_id::STRUCT) { + CUDF_EXPECTS( + std::all_of(col.child_begin(), + col.child_end(), + [](auto const& child) { return child.type().id() != type_id::LIST; }), + "Structs columns should be decomposed before reaching this function."); + } + // We are looking for lists-of-structs. if (col.type().id() != type_id::LIST) { return false; } @@ -697,8 +708,8 @@ std::shared_ptr preprocessed_table::create( [[maybe_unused]] auto [transformed_t, unused_0, structs_transformed_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, stream); - auto const ranked_floating_point = - structs_transformed_columns.size() > 0 && lists_of_structs_have_floating_point(input); + auto const ranked_floating_point = structs_transformed_columns.size() > 0 && + lists_of_structs_have_floating_point(decomposed_input); return create_preprocessed_table(transformed_t, std::move(verticalized_col_depths), @@ -739,10 +750,10 @@ preprocessed_table::create(table_view const& lhs, structs_transformed_columns_rhs] = transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); - auto const ranked_floating_point_lhs = - structs_transformed_columns_lhs.size() > 0 && lists_of_structs_have_floating_point(lhs); - auto const ranked_floating_point_rhs = - structs_transformed_columns_rhs.size() > 0 && lists_of_structs_have_floating_point(rhs); + auto const ranked_floating_point_lhs = structs_transformed_columns_lhs.size() > 0 && + lists_of_structs_have_floating_point(decomposed_lhs); + auto const ranked_floating_point_rhs = structs_transformed_columns_rhs.size() > 0 && + lists_of_structs_have_floating_point(decomposed_rhs); // printf("line %d\n", __LINE__); // cudf::test::print(transformed_lhs.column(0)); From 3bbb1ee1cda41e1feeceae275e88128c623770cc Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 13:38:01 -0700 Subject: [PATCH 107/140] Fix docs --- .../cudf/table/experimental/row_operators.cuh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 43b0a39135c..6b312af16bf 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -690,9 +690,9 @@ struct preprocessed_table { * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns * are sorted in ascending order. - * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If it is nullptr, then null precedence - * would be `null_order::BEFORE` for all columns. + * @param null_precedence Optional, an array having the same length as the number of columns in + * the input tables that indicates how null values compare to all other. If it is empty, + * the order `null_order::BEFORE` will be used for all columns. * @param stream The stream to launch kernels and h->d copies on while preprocessing. * @return A shared pointer to a preprocessed table. */ @@ -716,9 +716,9 @@ struct preprocessed_table { * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns * are sorted in ascending order. - * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If it is nullptr, then null precedence - * would be `null_order::BEFORE` for all columns. + * @param null_precedence Optional, an array having the same length as the number of columns in + * the input tables that indicates how null values compare to all other. If it is empty, + * the order `null_order::BEFORE` will be used for all columns. * @param stream The stream to launch kernels and h->d copies on while preprocessing. * @return A pair of shared pointers to the preprocessed tables */ @@ -744,9 +744,9 @@ struct preprocessed_table { * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns * are sorted in ascending order. - * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If it is nullptr, then null precedence - * would be `null_order::BEFORE` for all columns. + * @param null_precedence Optional, an array having the same length as the number of columns in + * the input tables that indicates how null values compare to all other. If it is empty, + * the order `null_order::BEFORE` will be used for all columns. * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank`. * @param stream The stream to launch kernels and h->d copies on while preprocessing. From ee92dba4335789a0b583343869bed87805ea1642 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 13:43:39 -0700 Subject: [PATCH 108/140] Add column order to rank computation --- cpp/src/table/row_operators.cu | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 1329a8793ee..d34e537ed33 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -394,6 +394,7 @@ namespace { * * @param lhs The input lhs column to transform * @param rhs The input rhs column to transform (if available) + * @param column_null_order The flag indicating how nulls compare to non-null values * @param stream CUDA stream used for device memory operations and kernel launches * @return A tuple consisting of new column_view representing the transformed input, along with * their ranks column(s) (of `size_type` type) and possibly new list offsets generated @@ -405,6 +406,7 @@ std::tuple>> transform_lists_of_structs(column_view const& lhs, std::optional const& rhs_opt, + null_order column_null_order, rmm::cuda_stream_view stream) { auto const default_mr = rmm::mr::get_current_device_resource(); @@ -443,18 +445,17 @@ transform_lists_of_structs(column_view const& lhs, // With dense rank, `transformed_input = [ [0, 2], [0, 1] ]`, producing correct comparison. // // In addition, since the ranked structs column(s) are nested child column instead of - // top-level column, the column order and null order should be fixed to the same values in all - // situations. + // top-level column, the column order should be fixed to the same values in all situations. // For example, with the same input above, using the fixed values for column order - // (`order::ASCENDING`) and null order (`null_order::BEFORE`), we have - // `transformed_input = [ [0, 2], [0, 1] ]`. Sorting of `transformed_input` will produce the - // same result as sorting `input` regardless of sorting order (ASC or DESC). + // (`order::ASCENDING`), we have `transformed_input = [ [0, 2], [0, 1] ]`. Sorting of + // `transformed_input` will produce the same result as sorting `input` regardless of sorting + // order (ASC or DESC). auto const compute_ranks = [&](column_view const& input) { return cudf::detail::rank(input, rank_method::DENSE, order::ASCENDING, null_policy::EXCLUDE, - null_order::BEFORE, + column_null_order, false /*percentage*/, stream, default_mr); @@ -510,7 +511,7 @@ transform_lists_of_structs(column_view const& lhs, // Recursively call transformation on the child column. auto [new_child_lhs, new_child_rhs_opt, out_cols_child_lhs, out_cols_child_rhs] = - transform_lists_of_structs(child_lhs, child_rhs_opt, stream); + transform_lists_of_structs(child_lhs, child_rhs_opt, column_null_order, stream); // Only transform the current pair of columns if their children have been transformed. if (out_cols_child_lhs.size() > 0 || out_cols_child_rhs.size() > 0) { @@ -562,6 +563,9 @@ transform_lists_of_structs(column_view const& lhs, * * @param lhs The input lhs table to transform * @param rhs The input rhs table to transform (if available) + * @param null_precedence Optional, an array having the same length as the number of columns in + * the input tables that indicates how null values compare to all other. If it is empty, + * the order `null_order::BEFORE` will be used for all columns. * @param stream CUDA stream used for device memory operations and kernel launches * @return A tuple consisting of new table_view representing the transformed input, along with * the ranks column (of `size_type` type) and possibly new list offsets generated during the @@ -573,6 +577,7 @@ std::tuple>> transform_lists_of_structs(table_view const& lhs, std::optional const& rhs, + host_span null_precedence, rmm::cuda_stream_view stream) { if (lhs.num_rows() == 0 && (!rhs || rhs.value().num_rows() == 0)) { @@ -591,7 +596,11 @@ transform_lists_of_structs(table_view const& lhs, rhs ? std::optional{rhs.value().column(col_idx)} : std::nullopt; auto [transformed_lhs, transformed_rhs_opt, curr_out_cols_lhs, curr_out_cols_rhs] = - transform_lists_of_structs(lhs_col, rhs_col_opt, stream); + transform_lists_of_structs( + lhs_col, + rhs_col_opt, + null_precedence.empty() ? null_order::BEFORE : null_precedence[col_idx], + stream); transformed_lhs_cvs.push_back(transformed_lhs); if (rhs) { transformed_rhs_cvs.push_back(transformed_rhs_opt.value()); } @@ -649,7 +658,7 @@ bool lists_of_structs_have_floating_point(table_view const& input) return true; } - // Found a lists of some-type other than STRUCT or LIST. + // Found a lists column of some-type other than STRUCT or LIST. return false; }); } @@ -707,7 +716,7 @@ std::shared_ptr preprocessed_table::create( decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); [[maybe_unused]] auto [transformed_t, unused_0, structs_transformed_columns, unused_1] = - transform_lists_of_structs(decomposed_input, std::nullopt, stream); + transform_lists_of_structs(decomposed_input, std::nullopt, new_null_precedence, stream); auto const ranked_floating_point = structs_transformed_columns.size() > 0 && lists_of_structs_have_floating_point(decomposed_input); @@ -748,7 +757,7 @@ preprocessed_table::create(table_view const& lhs, transformed_rhs_opt, structs_transformed_columns_lhs, structs_transformed_columns_rhs] = - transform_lists_of_structs(decomposed_lhs, decomposed_rhs, stream); + transform_lists_of_structs(decomposed_lhs, decomposed_rhs, new_null_precedence_lhs, stream); auto const ranked_floating_point_lhs = structs_transformed_columns_lhs.size() > 0 && lists_of_structs_have_floating_point(decomposed_lhs); From d0c04b9f9dddb9843ec20f3e998c5df015f8de22 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 13:54:31 -0700 Subject: [PATCH 109/140] Rewrite docs --- .../cudf/table/experimental/row_operators.cuh | 13 ++++++------ cpp/src/table/row_operators.cu | 20 +++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 6b312af16bf..d943e14ff4d 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -739,8 +739,8 @@ struct preprocessed_table { * @param preprocessed_input The table resulted from preprocessing * @param verticalized_col_depths The depths of each column resulting from decomposing struct * columns in the original input table - * @param structs_transformed_columns Store the intermediate results from transforming the child - * columns of lists-of-structs columns into integer columns using `cudf::rank()` + * @param structs_transformed_columns Store the intermediate columns generated from transforming + * lists-of-structs columns into lists-of-integers columns using `cudf::rank()`. * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns * are sorted in ascending order. @@ -781,9 +781,8 @@ struct preprocessed_table { * contain an empty `dremel_device_view`. As such, this uvector has as many elements as * there are columns in the table (unlike the `dremel_data` parameter, which is only as * long as the number of list columns). - * @param structs_transformed_columns Store the intermediate results from transforming the child - * columns of lists-of-structs columns into integer columns using `cudf::rank()` and will - * be used for row comparison. + * @param structs_transformed_columns Store the intermediate columns generated from transforming + * lists-of-structs columns into lists-of-integers columns using `cudf::rank()`. * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank`. */ @@ -867,8 +866,8 @@ struct preprocessed_table { std::optional> _dremel_data; std::optional> _dremel_device_views; - // Intermediate columns generated from transforming the child columns of lists-of-structs columns - // into columns of `size_type` type using `cudf::rank()`, need to be kept alive. + // Intermediate columns generated from transforming lists-of-structs columns into + // lists-of-integers columns using `cudf::rank()`, need to be kept alive. std::vector> _structs_transformed_columns; // Flag to record if the input table was preprocessed to transform any lists-of-structs column diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index d34e537ed33..5dde690bc9d 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -715,11 +715,12 @@ std::shared_ptr preprocessed_table::create( auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); + // Unused variables are generated for rhs table which is not available here. [[maybe_unused]] auto [transformed_t, unused_0, structs_transformed_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, new_null_precedence, stream); + auto const ranked_floating_point = structs_transformed_columns.size() > 0 && lists_of_structs_have_floating_point(decomposed_input); - return create_preprocessed_table(transformed_t, std::move(verticalized_col_depths), std::move(structs_transformed_columns), @@ -743,15 +744,12 @@ preprocessed_table::create(table_view const& lhs, new_null_precedence_lhs, verticalized_col_depths_lhs] = decompose_structs(lhs, false /*no decompose lists*/, column_order, null_precedence); + + // Unused variables are new column order and null order for rhs, which are the same as for lhs + // so we don't need them. [[maybe_unused]] auto [decomposed_rhs, unused0, unused1, verticalized_col_depths_rhs] = decompose_structs(rhs, false /*no decompose lists*/, column_order, null_precedence); - // printf("line %d\n", __LINE__); - // cudf::test::print(decomposed_lhs.column(0)); - - // printf("line %d\n", __LINE__); - // cudf::test::print(decomposed_rhs.column(0)); - // Transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_lhs, transformed_rhs_opt, @@ -759,17 +757,13 @@ preprocessed_table::create(table_view const& lhs, structs_transformed_columns_rhs] = transform_lists_of_structs(decomposed_lhs, decomposed_rhs, new_null_precedence_lhs, stream); + // This should be the same for both lhs and rhs but not all the time, such as when one table + // has 0 rows so we check separately for each of them. auto const ranked_floating_point_lhs = structs_transformed_columns_lhs.size() > 0 && lists_of_structs_have_floating_point(decomposed_lhs); auto const ranked_floating_point_rhs = structs_transformed_columns_rhs.size() > 0 && lists_of_structs_have_floating_point(decomposed_rhs); - // printf("line %d\n", __LINE__); - // cudf::test::print(transformed_lhs.column(0)); - - // printf("line %d\n", __LINE__); - // cudf::test::print(transformed_rhs_opt.value().column(0)); - return {create_preprocessed_table(transformed_lhs, std::move(verticalized_col_depths_lhs), std::move(structs_transformed_columns_lhs), From 17473b4f6fc7b965a22ef48fe32e80083f82d0fd Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 14:20:53 -0700 Subject: [PATCH 110/140] Add tests with different null orders --- cpp/tests/sort/sort_nested_types_tests.cpp | 48 +++++++++++++++++----- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index 576dce4bd2a..14ab9d4ceab 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -266,13 +266,14 @@ TEST_F(NestedStructTest, SimpleStructsOfListsOfStructsNoNulls) { auto const expected_order = int32s_col{3, 5, 2, 7, 0, 1, 6, 4}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + auto const order = cudf::stable_sorted_order(cudf::table_view{{*input}}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } { auto const expected_order = int32s_col{4, 6, 1, 0, 7, 2, 5, 3}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + auto const order = + cudf::stable_sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } } @@ -294,13 +295,14 @@ TEST_F(NestedListTest, SimpleListsOfStructsNoNulls) { auto const expected_order = int32s_col{3, 5, 2, 7, 0, 1, 6, 4}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + auto const order = cudf::stable_sorted_order(cudf::table_view{{*input}}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } { auto const expected_order = int32s_col{4, 6, 1, 0, 7, 2, 5, 3}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + auto const order = + cudf::stable_sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } } @@ -326,13 +328,14 @@ TEST_F(NestedListTest, SlicedListsOfStructsNoNulls) { auto const expected_order = int32s_col{3, 5, 2, 7, 0, 1, 6, 4}; - auto const order = cudf::sorted_order(cudf::table_view{{input}}); + auto const order = cudf::stable_sorted_order(cudf::table_view{{input}}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } { auto const expected_order = int32s_col{4, 6, 1, 0, 7, 2, 5, 3}; - auto const order = cudf::sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); + auto const order = + cudf::stable_sorted_order(cudf::table_view{{input}}, {cudf::order::DESCENDING}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } } @@ -364,6 +367,14 @@ TEST_F(NestedListTest, ListsOfEqualStructsNoNulls) TEST_F(NestedListTest, SimpleListsOfStructsWithNulls) { + // [ {null, 2}, {null, null}, {1, 2} ] | 0 + // [] | 1 + // [ {null, null}, {4, 2} ] | 2 + // [] | 3 + // [ {3, 5}, {null, 4} ] | 4 + // [] | 5 + // [ {5, 3}, {5, 0}, {1, 1} ] | 6 + // [ {null, 3}, {5, 2}, {4, 2} ] | 7 auto const input = [] { auto const get_structs = [] { auto child0 = int32s_col{{null, null, 1, null, 4, 3, null, 5, 5, 1, null, 5, 4}, @@ -377,13 +388,29 @@ TEST_F(NestedListTest, SimpleListsOfStructsWithNulls) { auto const expected_order = int32s_col{1, 3, 5, 2, 0, 7, 4, 6}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + auto const order = cudf::stable_sorted_order( + cudf::table_view{{*input}}, {cudf::order::ASCENDING}, {cudf::null_order::BEFORE}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } { auto const expected_order = int32s_col{6, 4, 7, 0, 2, 1, 3, 5}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + auto const order = cudf::stable_sorted_order( + cudf::table_view{{*input}}, {cudf::order::DESCENDING}, {cudf::null_order::BEFORE}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{1, 3, 5, 2, 4, 6, 0, 7}; + auto const order = cudf::stable_sorted_order( + cudf::table_view{{*input}}, {cudf::order::ASCENDING}, {cudf::null_order::AFTER}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); + } + + { + auto const expected_order = int32s_col{7, 0, 6, 4, 2, 1, 3, 5}; + auto const order = cudf::stable_sorted_order( + cudf::table_view{{*input}}, {cudf::order::DESCENDING}, {cudf::null_order::AFTER}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } } @@ -408,13 +435,14 @@ TEST_F(NestedListTest, ListsOfListsOfStructsNoNulls) { auto const expected_order = int32s_col{3, 6, 5, 0, 1, 7, 4, 2}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}); + auto const order = cudf::stable_sorted_order(cudf::table_view{{*input}}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } { auto const expected_order = int32s_col{2, 4, 7, 1, 0, 5, 3, 6}; - auto const order = cudf::sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); + auto const order = + cudf::stable_sorted_order(cudf::table_view{{*input}}, {cudf::order::DESCENDING}); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_order, order->view()); } } From c7178b7ccf0ac821b70e2e705a1bdcc3f712670f Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 17:33:08 -0700 Subject: [PATCH 111/140] Add sort tests --- .../table/experimental_row_operator_tests.cu | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/cpp/tests/table/experimental_row_operator_tests.cu b/cpp/tests/table/experimental_row_operator_tests.cu index 1f3f7eefe79..16ff6dcf5b4 100644 --- a/cpp/tests/table/experimental_row_operator_tests.cu +++ b/cpp/tests/table/experimental_row_operator_tests.cu @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,8 @@ #include #include +#include +#include #include #include @@ -185,6 +188,34 @@ auto two_table_equality(cudf::table_view lhs, return output; } +template +auto sort_table( + std::shared_ptr preprocessed_input, + cudf::size_type num_rows, + bool has_nested, + PhysicalElementComparator comparator, + rmm::cuda_stream_view stream) +{ + auto output = cudf::make_numeric_column(cudf::data_type(cudf::type_to_id()), + num_rows, + cudf::mask_state::UNALLOCATED, + stream); + auto const out_begin = output->mutable_view().begin(); + thrust::sequence(rmm::exec_policy(stream), out_begin, out_begin + num_rows, 0); + + auto const table_comparator = + cudf::experimental::row::lexicographic::self_comparator{preprocessed_input}; + if (has_nested) { + auto const comp = table_comparator.less(cudf::nullate::NO{}, comparator); + thrust::stable_sort(rmm::exec_policy(stream), out_begin, out_begin + num_rows, comp); + } else { + auto const comp = table_comparator.less(cudf::nullate::NO{}, comparator); + thrust::stable_sort(rmm::exec_policy(stream), out_begin, out_begin + num_rows, comp); + } + + return output; +} + TYPED_TEST(TypedTableViewTest, TestLexicographicalComparatorTwoTables) { using T = TypeParam; @@ -230,6 +261,159 @@ TYPED_TEST(TypedTableViewTest, TestLexicographicalComparatorSameTable) CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, sorting_got->view()); } +TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTables) +{ + using data_col = cudf::test::fixed_width_column_wrapper; + using int32s_col = cudf::test::fixed_width_column_wrapper; + + auto const col1 = data_col{5, 2, 7, 1, 3}; + auto const col2 = data_col{}; // empty + auto const lhs = cudf::table_view{{col1}}; + auto const rhs = cudf::table_view{{col2}}; + + auto const stream = cudf::get_default_stream(); + auto const test_sort = [stream](auto const& preprocessed, + auto const& input, + auto const& comparator, + auto const& expected) { + auto const output = sort_table( + preprocessed, input.num_rows(), cudf::detail::has_nested_columns(input), comparator, stream); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, output->view()); + }; + + auto const test_sort_two_tables = [&](auto const& preprocessed_lhs, + auto const& preprocessed_rhs) { + auto const expected_lhs = int32s_col{3, 1, 4, 0, 2}; + test_sort(preprocessed_lhs, + lhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_lhs); + test_sort(preprocessed_lhs, + lhs, + cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, + expected_lhs); + + auto const expected_rhs = int32s_col{}; + test_sort(preprocessed_rhs, + rhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_rhs); + test_sort(preprocessed_rhs, + rhs, + cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, + expected_rhs); + }; + + // Generate preprocessed data for both lhs and lhs at the same time. + // Switching order of lhs and rhs tables then sorting them using their preprocessed data should + // produce exactly the same result. + { + auto const [preprocessed_lhs, preprocessed_rhs /*empty*/] = + cudf::experimental::row::lexicographic::preprocessed_table::create( + lhs, rhs /*empty*/, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + } + { + auto const [preprocessed_rhs /*empty*/, preprocessed_lhs] = + cudf::experimental::row::lexicographic::preprocessed_table::create( + rhs /*empty*/, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + } +} + +TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) +{ + using data_col = cudf::test::fixed_width_column_wrapper; + using int32s_col = cudf::test::fixed_width_column_wrapper; + using strings_col = cudf::test::strings_column_wrapper; + using structs_col = cudf::test::structs_column_wrapper; + + auto const col1 = [] { + auto const get_structs = [] { + auto child0 = data_col{0, 3, 0, 2}; + auto child1 = strings_col{"a", "c", "a", "b"}; + return structs_col{{child0, child1}}; + }; + return cudf::make_lists_column( + 2, int32s_col{0, 2, 4}.release(), get_structs().release(), 0, {}); + }(); + auto const col2 = [] { + auto const get_structs = [] { + auto child0 = data_col{}; + auto child1 = strings_col{}; + return structs_col{{child0, child1}}; + }; + return cudf::make_lists_column(0, int32s_col{}.release(), get_structs().release(), 0, {}); + }(); + + auto const column_order = std::vector{cudf::order::ASCENDING}; + auto const lhs = cudf::table_view{{*col1}}; + auto const rhs = cudf::table_view{{*col2}}; + + auto const stream = cudf::get_default_stream(); + auto const test_sort = [stream](auto const& preprocessed, + auto const& input, + auto const& comparator, + auto const& expected) { + auto const output = sort_table( + preprocessed, input.num_rows(), cudf::detail::has_nested_columns(input), comparator, stream); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, output->view()); + }; + + auto const test_sort_two_tables = [&](auto const& preprocessed_lhs, + auto const& preprocessed_rhs) { + auto const expected_lhs = int32s_col{1, 0}; + test_sort(preprocessed_lhs, + lhs, + cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, + expected_lhs); + + auto const expected_rhs = int32s_col{}; + test_sort(preprocessed_rhs, + rhs, + cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, + expected_rhs); + + if constexpr (std::is_floating_point_v) { + EXPECT_THROW(test_sort(preprocessed_lhs, + lhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_lhs), + cudf::logic_error); + EXPECT_THROW(test_sort(preprocessed_rhs, + rhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_rhs), + cudf::logic_error); + } else { + test_sort(preprocessed_lhs, + lhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_lhs); + test_sort(preprocessed_rhs, + rhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_rhs); + } + }; + + // Generate preprocessed data for both lhs and lhs at the same time. + // Switching order of lhs and rhs tables then sorting them using their preprocessed data should + // produce exactly the same result. + { + auto const [preprocessed_lhs, preprocessed_rhs /*empty*/] = + cudf::experimental::row::lexicographic::preprocessed_table::create( + lhs, rhs /*empty*/, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + } + { + auto const [preprocessed_rhs /*empty*/, preprocessed_lhs] = + cudf::experimental::row::lexicographic::preprocessed_table::create( + rhs /*empty*/, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + } +} + template struct NaNTableViewTest : public cudf::test::BaseFixture { }; From b8d012d97f2c1bafa52875135d7a1247bb4671ce Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 20:54:22 -0700 Subject: [PATCH 112/140] Implement `preprocessed_id` --- .../cudf/table/experimental/row_operators.cuh | 24 +++++++++++++++---- cpp/src/table/row_operators.cu | 20 ++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index d943e14ff4d..cf3fb6b6a78 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -747,6 +747,7 @@ struct preprocessed_table { * @param null_precedence Optional, an array having the same length as the number of columns in * the input tables that indicates how null values compare to all other. If it is empty, * the order `null_order::BEFORE` will be used for all columns. + * @param preprocessed_id A randomly generated ID number * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank`. * @param stream The stream to launch kernels and h->d copies on while preprocessing. @@ -757,6 +758,7 @@ struct preprocessed_table { std::vector>&& structs_transformed_columns, host_span column_order, host_span null_precedence, + uint64_t preprocessed_id, bool ranked_floating_point, rmm::cuda_stream_view stream); @@ -783,6 +785,7 @@ struct preprocessed_table { * long as the number of list columns). * @param structs_transformed_columns Store the intermediate columns generated from transforming * lists-of-structs columns into lists-of-integers columns using `cudf::rank()`. + * @param preprocessed_id A randomly generated ID number * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank`. */ @@ -793,6 +796,7 @@ struct preprocessed_table { std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::vector>&& structs_transformed_columns, + uint64_t preprocessed_id, bool ranked_floating_point); preprocessed_table(table_device_view_owner&& table, @@ -800,6 +804,7 @@ struct preprocessed_table { rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::vector>&& structs_transformed_columns, + uint64_t preprocessed_id, bool ranked_floating_point); /** @@ -870,6 +875,12 @@ struct preprocessed_table { // lists-of-integers columns using `cudf::rank()`, need to be kept alive. std::vector> _structs_transformed_columns; + // An ID number generated by the factory functions `preprocess::create()`. + // If two preprocessed_table(s) are generated together using the factory function + // `preprocessed_table::create(table_view const&, table_view const&)`, they will be assigned the + // same ID number. + uint64_t _preprocessed_id; + // Flag to record if the input table was preprocessed to transform any lists-of-structs column // having at least one floating-point child (at any nested level) using `cudf::rank`. bool const _ranked_floating_point; @@ -1086,11 +1097,16 @@ class two_table_comparator { std::shared_ptr right) : d_left_table{std::move(left)}, d_right_table{std::move(right)} { + // If we want to use preprocessed_table(s) that have transformed structs columns for two-table + // comparator, they must have been generated together using the factory function + // `preprocessed_table::create(table_view const&, table_view const&)`. + // We can check for that using their _preprocessed_id numbers. CUDF_EXPECTS( - d_left_table->_ranked_floating_point == d_right_table->_ranked_floating_point || - (!d_left_table->_ranked_floating_point && d_left_table->_t->num_rows() == 0) || - (!d_right_table->_ranked_floating_point && d_right_table->_t->num_rows() == 0), - "The pre-generated preprocessed_table(s) are not be able to use for two_table_comparator."); + (d_left_table->_structs_transformed_columns.size() == 0 && + d_right_table->_structs_transformed_columns.size() == 0) || + (d_left_table->_preprocessed_id == d_right_table->_preprocessed_id), + "The pre-generated preprocessed_table(s) are not be able to use for two_table_comparator.\n" + "Please create a two_table_comparator using the original tables instead."); } /** diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 5dde690bc9d..e8fb03a3ee8 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -34,6 +34,8 @@ #include +#include + namespace cudf { namespace experimental { @@ -663,6 +665,13 @@ bool lists_of_structs_have_floating_point(table_view const& input) }); } +uint64_t generate_random_id() +{ + auto gen = std::mt19937{std::random_device{}()}; + std::uniform_int_distribution dis; + return dis(gen); +} + } // namespace std::shared_ptr preprocessed_table::create_preprocessed_table( @@ -671,6 +680,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::vector>&& structs_transformed_columns, host_span column_order, host_span null_precedence, + uint64_t preprocessed_id, bool ranked_floating_point, rmm::cuda_stream_view stream) { @@ -694,6 +704,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(dremel_data), std::move(d_dremel_device_view), std::move(structs_transformed_columns), + preprocessed_id, ranked_floating_point)); } else { return std::shared_ptr( @@ -702,6 +713,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(d_null_precedence), std::move(d_depths), std::move(structs_transformed_columns), + preprocessed_id, ranked_floating_point)); } } @@ -726,6 +738,7 @@ std::shared_ptr preprocessed_table::create( std::move(structs_transformed_columns), new_column_order, new_null_precedence, + generate_random_id(), ranked_floating_point, stream); } @@ -763,12 +776,14 @@ preprocessed_table::create(table_view const& lhs, lists_of_structs_have_floating_point(decomposed_lhs); auto const ranked_floating_point_rhs = structs_transformed_columns_rhs.size() > 0 && lists_of_structs_have_floating_point(decomposed_rhs); + auto const preprocessed_id = generate_random_id(); return {create_preprocessed_table(transformed_lhs, std::move(verticalized_col_depths_lhs), std::move(structs_transformed_columns_lhs), new_column_order_lhs, new_null_precedence_lhs, + preprocessed_id, ranked_floating_point_lhs, stream), create_preprocessed_table(transformed_rhs_opt.value(), @@ -776,6 +791,7 @@ preprocessed_table::create(table_view const& lhs, std::move(structs_transformed_columns_rhs), new_column_order_lhs, new_null_precedence_lhs, + preprocessed_id, ranked_floating_point_rhs, stream)}; } @@ -788,6 +804,7 @@ preprocessed_table::preprocessed_table( std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::vector>&& structs_transformed_columns, + uint64_t preprocessed_id, bool ranked_floating_point) : _t(std::move(table)), _column_order(std::move(column_order)), @@ -796,6 +813,7 @@ preprocessed_table::preprocessed_table( _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), _structs_transformed_columns(std::move(structs_transformed_columns)), + _preprocessed_id(preprocessed_id), _ranked_floating_point(ranked_floating_point) { } @@ -806,6 +824,7 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::vector>&& structs_transformed_columns, + uint64_t preprocessed_id, bool ranked_floating_point) : _t(std::move(table)), _column_order(std::move(column_order)), @@ -814,6 +833,7 @@ preprocessed_table::preprocessed_table( _dremel_data{}, _dremel_device_views{}, _structs_transformed_columns(std::move(structs_transformed_columns)), + _preprocessed_id(preprocessed_id), _ranked_floating_point(ranked_floating_point) { } From 984317e78ffc20af8aec5c382d0859cfb4057679 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 21:00:58 -0700 Subject: [PATCH 113/140] Update test --- .../table/experimental_row_operator_tests.cu | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cpp/tests/table/experimental_row_operator_tests.cu b/cpp/tests/table/experimental_row_operator_tests.cu index 16ff6dcf5b4..69213047643 100644 --- a/cpp/tests/table/experimental_row_operator_tests.cu +++ b/cpp/tests/table/experimental_row_operator_tests.cu @@ -412,6 +412,26 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) rhs /*empty*/, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); } + + // Test creating two_table_comparator using preprocessed_table(s). + { + auto const [preprocessed_lhs, preprocessed_rhs] = + cudf::experimental::row::lexicographic::preprocessed_table::create( + lhs, rhs, std::vector{cudf::order::ASCENDING}, {}, stream); + EXPECT_NO_THROW(cudf::experimental::row::lexicographic::two_table_comparator(preprocessed_lhs, + preprocessed_rhs)); + } + { + auto const preprocessed_lhs = + cudf::experimental::row::lexicographic::preprocessed_table::create( + lhs, std::vector{cudf::order::ASCENDING}, {}, stream); + auto const preprocessed_rhs = + cudf::experimental::row::lexicographic::preprocessed_table::create( + rhs, std::vector{cudf::order::ASCENDING}, {}, stream); + EXPECT_THROW(cudf::experimental::row::lexicographic::two_table_comparator(preprocessed_lhs, + preprocessed_rhs), + cudf::logic_error); + } } template From 457f5936458834cb6ad10edba4b1f5de0c471641 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 21:11:55 -0700 Subject: [PATCH 114/140] Fix error with tables having 0 row --- cpp/src/table/row_operators.cu | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index e8fb03a3ee8..d247392a291 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -582,11 +582,6 @@ transform_lists_of_structs(table_view const& lhs, host_span null_precedence, rmm::cuda_stream_view stream) { - if (lhs.num_rows() == 0 && (!rhs || rhs.value().num_rows() == 0)) { - return { - lhs, rhs, std::vector>{}, std::vector>{}}; - } - std::vector transformed_lhs_cvs; std::vector transformed_rhs_cvs; std::vector> out_cols_lhs; From dce8a72c38218281b4d8f6dafe2e07a3926328d2 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 21:15:59 -0700 Subject: [PATCH 115/140] Add comment --- cpp/src/table/row_operators.cu | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index d247392a291..5304bba9fb4 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -771,6 +771,9 @@ preprocessed_table::create(table_view const& lhs, lists_of_structs_have_floating_point(decomposed_lhs); auto const ranked_floating_point_rhs = structs_transformed_columns_rhs.size() > 0 && lists_of_structs_have_floating_point(decomposed_rhs); + + // The same ID will be assigned to all preprocessed_table(s) so we can know later if they are + // compatible to compare against each other. auto const preprocessed_id = generate_random_id(); return {create_preprocessed_table(transformed_lhs, From e27f9c1eac54ccc0279a62399281c9654e7c88cf Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 21:18:03 -0700 Subject: [PATCH 116/140] MISC --- cpp/src/table/row_operators.cu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 5304bba9fb4..e9e42c47d1e 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -662,9 +662,9 @@ bool lists_of_structs_have_floating_point(table_view const& input) uint64_t generate_random_id() { - auto gen = std::mt19937{std::random_device{}()}; - std::uniform_int_distribution dis; - return dis(gen); + auto gen = std::mt19937{std::random_device{}()}; + auto dist = std::uniform_int_distribution{}; + return dist(gen); } } // namespace From e1b8c694455809b03e0284edde14c7aae3288e3e Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 21:21:01 -0700 Subject: [PATCH 117/140] Reverse verbosity level --- cpp/tests/search/search_list_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index bdab6e5d4ec..90619e4456d 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -33,7 +33,7 @@ using int32s_col = cudf::test::fixed_width_column_wrapper; using structs_col = cudf::test::structs_column_wrapper; using strings_col = cudf::test::strings_column_wrapper; -constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::ALL_ERRORS}; +constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::FIRST_ERROR}; constexpr int32_t null{0}; // Mark for null child elements at the current level constexpr int32_t XXX{0}; // Mark for null elements at all levels constexpr int32_t dont_care{0}; // Mark for elements that will be sliced off From 2e79bed904cd7be616f40b340d51c4b431cdf703 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Thu, 13 Apr 2023 21:52:44 -0700 Subject: [PATCH 118/140] Fix comment --- cpp/src/table/row_operators.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index e9e42c47d1e..d6cb4f63b46 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -400,7 +400,7 @@ namespace { * @param stream CUDA stream used for device memory operations and kernel launches * @return A tuple consisting of new column_view representing the transformed input, along with * their ranks column(s) (of `size_type` type) and possibly new list offsets generated - * during the transformation process + * during the transformation process */ std::tuple, From 9709738cca8fc88cb377047ed45010a993c1f984 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 14 Apr 2023 06:22:23 -0700 Subject: [PATCH 119/140] Update docs --- .../cudf/table/experimental/row_operators.cuh | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index cf3fb6b6a78..e3efb144c13 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -953,8 +953,11 @@ class self_comparator { * overloads for primitive types. * @tparam Nullate A cudf::nullate type describing whether to check for nulls. * @tparam PhysicalElementComparator A relational comparator functor that compares individual - * values rather than logical elements, defaults to `NaN` aware relational comparator that - * evaluates `NaN` as greater than all other values. + * values rather than logical elements, defaults to `NaN` aware relational comparator + * that evaluates `NaN` as greater than all other values. + * @throw cudf::logic_error if the input table were preprocessed to transform any + * lists-of-structs column having floating-point children into lists-of-integers column, + * but `PhysicalElementComparator` is not `sorting_physical_element_comparator`. * @param nullate Indicates if any input column contains nulls. * @param comparator Physical element relational comparison functor. * @return A binary callable object. @@ -1136,8 +1139,11 @@ class two_table_comparator { * overloads for primitive types. * @tparam Nullate A cudf::nullate type describing whether to check for nulls. * @tparam PhysicalElementComparator A relational comparator functor that compares individual - * values rather than logical elements, defaults to `NaN` aware relational comparator that - * evaluates `NaN` as greater than all other values. + * values rather than logical elements, defaults to `NaN` aware relational comparator + * that evaluates `NaN` as greater than all other values. + * @throw cudf::logic_error if the input tables were preprocessed to transform any + * lists-of-structs column having floating-point children into lists-of-integers column, + * but `PhysicalElementComparator` is not `sorting_physical_element_comparator`. * @param nullate Indicates if any input column contains nulls. * @param comparator Physical element relational comparison functor. * @return A binary callable object. @@ -1145,8 +1151,14 @@ class two_table_comparator { template - auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const noexcept + auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { + if constexpr (!std::is_same_v) { + CUDF_EXPECTS(!d_t->_ranked_floating_point, + "The input table has floating-point numbers and was preprocessed using a " + "different type of physical element comparator."); + } + return less_comparator{strong_index_comparator_adapter{ device_row_comparator{ nullate, @@ -1164,9 +1176,14 @@ class two_table_comparator { template - auto less_equivalent(Nullate nullate = {}, - PhysicalElementComparator comparator = {}) const noexcept + auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { + if constexpr (!std::is_same_v) { + CUDF_EXPECTS(!d_t->_ranked_floating_point, + "The input table has floating-point numbers and was preprocessed using a " + "different type of physical element comparator."); + } + return less_equivalent_comparator{strong_index_comparator_adapter{ device_row_comparator{ nullate, From 8cf9b9688b56d17052941d22704b889c83d9dd82 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 14 Apr 2023 06:25:17 -0700 Subject: [PATCH 120/140] Other doc fixes --- .../cudf/table/experimental/row_operators.cuh | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index e3efb144c13..e5f50da2254 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -740,7 +740,7 @@ struct preprocessed_table { * @param verticalized_col_depths The depths of each column resulting from decomposing struct * columns in the original input table * @param structs_transformed_columns Store the intermediate columns generated from transforming - * lists-of-structs columns into lists-of-integers columns using `cudf::rank()`. + * lists-of-structs columns into lists-of-integers columns using `cudf::rank()` * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns * are sorted in ascending order. @@ -749,8 +749,8 @@ struct preprocessed_table { * the order `null_order::BEFORE` will be used for all columns. * @param preprocessed_id A randomly generated ID number * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform - * any lists-of-structs column having floating-point children using `cudf::rank`. - * @param stream The stream to launch kernels and h->d copies on while preprocessing. + * any lists-of-structs column having floating-point children using `cudf::rank` + * @param stream The stream to launch kernels and h->d copies on while preprocessing */ static std::shared_ptr create_preprocessed_table( table_view const& preprocessed_input, @@ -784,10 +784,10 @@ struct preprocessed_table { * there are columns in the table (unlike the `dremel_data` parameter, which is only as * long as the number of list columns). * @param structs_transformed_columns Store the intermediate columns generated from transforming - * lists-of-structs columns into lists-of-integers columns using `cudf::rank()`. + * lists-of-structs columns into lists-of-integers columns using `cudf::rank()` * @param preprocessed_id A randomly generated ID number * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform - * any lists-of-structs column having floating-point children using `cudf::rank`. + * any lists-of-structs column having floating-point children using `cudf::rank` */ preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, @@ -907,13 +907,13 @@ class self_comparator { * * @param t The table to compare * @param column_order Optional, host array the same length as a row that indicates the desired - * ascending/descending order of each column in a row. If empty, it is assumed all columns are - * sorted in ascending order. + * ascending/descending order of each column in a row. If empty, it is assumed all columns + * are sorted in ascending order. * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If empty, then null precedence would be - * `null_order::BEFORE` for all columns. + * values compare to all other for every column. If empty, then null precedence would be + * `null_order::BEFORE` for all columns. * @param stream The stream to construct this object on. Not the stream that will be used for - * comparisons using this object. + * comparisons using this object. */ self_comparator(table_view const& t, host_span column_order = {}, @@ -942,9 +942,9 @@ class self_comparator { * `F(i,j)` returns true if and only if row `i` compares lexicographically less than row `j`. * * @note The operator overloads in sub-class `element_comparator` are templated via the - * `type_dispatcher` to help select an overload instance for each column in a table. - * So, `cudf::is_nested` will return `true` if the table has nested-type columns, - * but it will be a runtime error if template parameter `has_nested_columns != true`. + * `type_dispatcher` to help select an overload instance for each column in a table. + * So, `cudf::is_nested` will return `true` if the table has nested-type columns, + * but it will be a runtime error if template parameter `has_nested_columns != true`. * * @tparam has_nested_columns compile-time optimization for primitive types. * This template parameter is to be used by the developer by querying @@ -1072,13 +1072,13 @@ class two_table_comparator { * @param left The left table to compare * @param right The right table to compare * @param column_order Optional, host array the same length as a row that indicates the desired - * ascending/descending order of each column in a row. If empty, it is assumed all columns are - * sorted in ascending order. + * ascending/descending order of each column in a row. If empty, it is assumed all columns + * are sorted in ascending order. * @param null_precedence Optional, device array the same length as a row and indicates how null - * values compare to all other for every column. If empty, then null precedence would be - * `null_order::BEFORE` for all columns. + * values compare to all other for every column. If empty, then null precedence would be + * `null_order::BEFORE` for all columns. * @param stream The stream to construct this object on. Not the stream that will be used for - * comparisons using this object. + * comparisons using this object. */ two_table_comparator(table_view const& left, table_view const& right, @@ -1128,9 +1128,9 @@ class two_table_comparator { * `j` of the left table. * * @note The operator overloads in sub-class `element_comparator` are templated via the - * `type_dispatcher` to help select an overload instance for each column in a table. - * So, `cudf::is_nested` will return `true` if the table has nested-type columns, - * but it will be a runtime error if template parameter `has_nested_columns != true`. + * `type_dispatcher` to help select an overload instance for each column in a table. + * So, `cudf::is_nested` will return `true` if the table has nested-type columns, + * but it will be a runtime error if template parameter `has_nested_columns != true`. * * @tparam has_nested_columns compile-time optimization for primitive types. * This template parameter is to be used by the developer by querying @@ -1154,8 +1154,8 @@ class two_table_comparator { auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_ranked_floating_point, - "The input table has floating-point numbers and was preprocessed using a " + CUDF_EXPECTS(!d_left_table->_ranked_floating_point && !d_right_table->_ranked_floating_point, + "The input tables have floating-point numbers and was preprocessed using a " "different type of physical element comparator."); } @@ -1179,8 +1179,8 @@ class two_table_comparator { auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_ranked_floating_point, - "The input table has floating-point numbers and was preprocessed using a " + CUDF_EXPECTS(!d_left_table->_ranked_floating_point && !d_right_table->_ranked_floating_point, + "The input tables have floating-point numbers and was preprocessed using a " "different type of physical element comparator."); } From fb17cc17ded658adbc8474fab69918f1b6f06ba8 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 18 Apr 2023 20:53:48 -0700 Subject: [PATCH 121/140] Fix style --- cpp/tests/sort/sort_nested_types_tests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index 3bca5bddd5a..c02b576a201 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -277,8 +277,7 @@ TEST_F(NestedStructTest, SimpleStructsOfListsOfStructsNoNulls) } } -struct NestedListTest : public cudf::test::BaseFixture { -}; +struct NestedListTest : public cudf::test::BaseFixture {}; TEST_F(NestedListTest, SimpleListsOfStructsNoNulls) { From 6ab28fa9effdf4376484dc1f221953fcb676fe63 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 18 Apr 2023 20:54:14 -0700 Subject: [PATCH 122/140] Change variable type --- cpp/benchmarks/sort/sort_lists.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/benchmarks/sort/sort_lists.cpp b/cpp/benchmarks/sort/sort_lists.cpp index 7927473ac12..01523423e10 100644 --- a/cpp/benchmarks/sort/sort_lists.cpp +++ b/cpp/benchmarks/sort/sort_lists.cpp @@ -21,8 +21,8 @@ #include namespace { -auto constexpr min_val = 0; -auto constexpr max_val = 100; +cudf::size_type constexpr min_val = 0; +cudf::size_type constexpr max_val = 100; void sort_multiple_lists(nvbench::state& state) { From bc955f0767e001cec65375fea63d55367cfe12f1 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 18 Apr 2023 20:54:21 -0700 Subject: [PATCH 123/140] Fix comments --- .../cudf/table/experimental/row_operators.cuh | 12 ++++++------ cpp/src/table/row_operators.cu | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index e5f50da2254..74f111d30bf 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -1104,12 +1104,12 @@ class two_table_comparator { // comparator, they must have been generated together using the factory function // `preprocessed_table::create(table_view const&, table_view const&)`. // We can check for that using their _preprocessed_id numbers. - CUDF_EXPECTS( - (d_left_table->_structs_transformed_columns.size() == 0 && - d_right_table->_structs_transformed_columns.size() == 0) || - (d_left_table->_preprocessed_id == d_right_table->_preprocessed_id), - "The pre-generated preprocessed_table(s) are not be able to use for two_table_comparator.\n" - "Please create a two_table_comparator using the original tables instead."); + CUDF_EXPECTS((d_left_table->_structs_transformed_columns.size() == 0 && + d_right_table->_structs_transformed_columns.size() == 0) || + (d_left_table->_preprocessed_id == d_right_table->_preprocessed_id), + "The independently generated preprocessed_table(s) are unable to be used with " + "two_table_comparator.\n" + "Please create a two_table_comparator using the original tables instead."); } /** diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index d6cb4f63b46..c5c48aa7aaf 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -166,7 +166,8 @@ table_view remove_struct_child_offsets(table_view table) * same row. * * @param table The table whose struct columns to decompose. - * @param decompose_lists Whether to decompose lists columns or output them unchanged + * @param decompose_lists Whether to decompose lists columns (preprocessing for equality comparator) + * or output them unchanged (preprocessing for lexicographic comparator) * @param column_order The per-column order if using output with lexicographic comparison * @param null_precedence The per-column null precedence * @return A tuple containing a table with all struct columns decomposed, new corresponding column @@ -325,12 +326,12 @@ void check_lex_compatibility(table_view const& input) if (c.type().id() == type_id::LIST) { auto const& list_col = lists_column_view(c); CUDF_EXPECTS(list_col.child().type().id() != type_id::STRUCT, - "Cannot lexicographic compare a table with a LIST of STRUCT column"); + "Cannot lexicographically compare a table with a LIST of STRUCT column"); check_column(list_col.child()); } else if (c.type().id() == type_id::STRUCT) { for (auto child = c.child_begin(); child < c.child_end(); ++child) { CUDF_EXPECTS(child->type().id() != type_id::LIST, - "Cannot lexicographic compare a table with a STRUCT of LIST column"); + "Cannot lexicographically compare a table with a STRUCT of LIST column"); check_column(*child); } } From 406db2a904b7f5c805231e6d9745e15a42470c17 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 18 Apr 2023 21:03:03 -0700 Subject: [PATCH 124/140] Add more comment --- cpp/include/cudf/table/experimental/row_operators.cuh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 74f111d30bf..78eff960c12 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -747,7 +747,9 @@ struct preprocessed_table { * @param null_precedence Optional, an array having the same length as the number of columns in * the input tables that indicates how null values compare to all other. If it is empty, * the order `null_order::BEFORE` will be used for all columns. - * @param preprocessed_id A randomly generated ID number + * @param preprocessed_id A randomly generated ID number by the factory functions + * `preprocess::create()`, used to check whether this preprocessed table was created + * independently or together with other table. * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank` * @param stream The stream to launch kernels and h->d copies on while preprocessing From 3e8e530b0e531f983246f87883180a07e630af05 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 09:50:07 -0700 Subject: [PATCH 125/140] Remove `preprocessed_id` and fix docs --- .../cudf/table/experimental/row_operators.cuh | 70 +++----- cpp/src/table/row_operators.cu | 159 +++++------------- 2 files changed, 67 insertions(+), 162 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 78eff960c12..0ab3edef453 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -739,29 +739,25 @@ struct preprocessed_table { * @param preprocessed_input The table resulted from preprocessing * @param verticalized_col_depths The depths of each column resulting from decomposing struct * columns in the original input table - * @param structs_transformed_columns Store the intermediate columns generated from transforming - * lists-of-structs columns into lists-of-integers columns using `cudf::rank()` + * @param transformed_columns Store the intermediate columns generated from transforming + * nested children columns into integers columns using `cudf::rank()` * @param column_order Optional, host array the same length as a row that indicates the desired * ascending/descending order of each column in a row. If empty, it is assumed all columns * are sorted in ascending order. * @param null_precedence Optional, an array having the same length as the number of columns in * the input tables that indicates how null values compare to all other. If it is empty, * the order `null_order::BEFORE` will be used for all columns. - * @param preprocessed_id A randomly generated ID number by the factory functions - * `preprocess::create()`, used to check whether this preprocessed table was created - * independently or together with other table. - * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform - * any lists-of-structs column having floating-point children using `cudf::rank` + * @param ranked_children Flag indicating if the input table was preprocessed to transform + * any nested child column into an integer column using `cudf::rank` * @param stream The stream to launch kernels and h->d copies on while preprocessing */ static std::shared_ptr create_preprocessed_table( table_view const& preprocessed_input, std::vector&& verticalized_col_depths, - std::vector>&& structs_transformed_columns, + std::vector>&& transformed_columns, host_span column_order, host_span null_precedence, - uint64_t preprocessed_id, - bool ranked_floating_point, + bool ranked_children, rmm::cuda_stream_view stream); /** @@ -787,8 +783,7 @@ struct preprocessed_table { * long as the number of list columns). * @param structs_transformed_columns Store the intermediate columns generated from transforming * lists-of-structs columns into lists-of-integers columns using `cudf::rank()` - * @param preprocessed_id A randomly generated ID number - * @param ranked_floating_point Flag indicating if the input table was preprocessed to transform + * @param ranked_children Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank` */ preprocessed_table(table_device_view_owner&& table, @@ -798,16 +793,14 @@ struct preprocessed_table { std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::vector>&& structs_transformed_columns, - uint64_t preprocessed_id, - bool ranked_floating_point); + bool ranked_children); preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::vector>&& structs_transformed_columns, - uint64_t preprocessed_id, - bool ranked_floating_point); + bool ranked_children); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -873,19 +866,13 @@ struct preprocessed_table { std::optional> _dremel_data; std::optional> _dremel_device_views; - // Intermediate columns generated from transforming lists-of-structs columns into - // lists-of-integers columns using `cudf::rank()`, need to be kept alive. - std::vector> _structs_transformed_columns; - - // An ID number generated by the factory functions `preprocess::create()`. - // If two preprocessed_table(s) are generated together using the factory function - // `preprocessed_table::create(table_view const&, table_view const&)`, they will be assigned the - // same ID number. - uint64_t _preprocessed_id; + // Intermediate columns generated from transforming nested children columns into + // integers columns using `cudf::rank()`, need to be kept alive. + std::vector> _transformed_columns; // Flag to record if the input table was preprocessed to transform any lists-of-structs column // having at least one floating-point child (at any nested level) using `cudf::rank`. - bool const _ranked_floating_point; + bool const _ranked_children; }; /** @@ -970,8 +957,8 @@ class self_comparator { auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_ranked_floating_point, - "The input table has floating-point numbers and was preprocessed using a " + CUDF_EXPECTS(!d_t->_ranked_children, + "The input table has nested type children and they were transformed using a " "different type of physical element comparator."); } @@ -995,8 +982,8 @@ class self_comparator { auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_ranked_floating_point, - "The input table has floating-point numbers and was preprocessed using a " + CUDF_EXPECTS(!d_t->_ranked_children, + "The input table has nested type children and they were transformed using a " "different type of physical element comparator."); } @@ -1095,6 +1082,11 @@ class two_table_comparator { * This constructor allows independently constructing a `preprocessed_table` and sharing it among * multiple comparators. * + * If the input tables have nested types such as lists-of-structs, + * which will be transformed into lists-of-integers during preprocesing, + * the preprocessed_table(s) must be generated together using the factory function + * `preprocessed_table::create(table_view const&, table_view const&)`. + * * @param left A table preprocessed for lexicographic comparison * @param right A table preprocessed for lexicographic comparison */ @@ -1102,16 +1094,6 @@ class two_table_comparator { std::shared_ptr right) : d_left_table{std::move(left)}, d_right_table{std::move(right)} { - // If we want to use preprocessed_table(s) that have transformed structs columns for two-table - // comparator, they must have been generated together using the factory function - // `preprocessed_table::create(table_view const&, table_view const&)`. - // We can check for that using their _preprocessed_id numbers. - CUDF_EXPECTS((d_left_table->_structs_transformed_columns.size() == 0 && - d_right_table->_structs_transformed_columns.size() == 0) || - (d_left_table->_preprocessed_id == d_right_table->_preprocessed_id), - "The independently generated preprocessed_table(s) are unable to be used with " - "two_table_comparator.\n" - "Please create a two_table_comparator using the original tables instead."); } /** @@ -1156,8 +1138,8 @@ class two_table_comparator { auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_left_table->_ranked_floating_point && !d_right_table->_ranked_floating_point, - "The input tables have floating-point numbers and was preprocessed using a " + CUDF_EXPECTS(!d_left_table->_ranked_children && !d_right_table->_ranked_children, + "The input tables have nested type children and they were transformed using a " "different type of physical element comparator."); } @@ -1181,8 +1163,8 @@ class two_table_comparator { auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_left_table->_ranked_floating_point && !d_right_table->_ranked_floating_point, - "The input tables have floating-point numbers and was preprocessed using a " + CUDF_EXPECTS(!d_left_table->_ranked_children && !d_right_table->_ranked_children, + "The input tables have nested type children and they were transformed using a " "different type of physical element comparator."); } diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index c5c48aa7aaf..f5acba5d1d7 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -34,8 +34,6 @@ #include -#include - namespace cudf { namespace experimental { @@ -145,8 +143,10 @@ table_view remove_struct_child_offsets(table_view table) * `List`, and `float`. * * When list columns are present, depending on the input flag `decompose_lists`, the decomposition - * can be performed similarly to pure structs but list parent columns are NOT pruned. For example, - * if the original table has a column `List>`, + * can be performed similarly to pure structs but list parent columns are NOT pruned. The list + * parents are still needed to define the range of elements in the leaf that belong to the same row. + * + * For example, if the original table has a column `List>`, * * L * | @@ -162,12 +162,13 @@ table_view remove_struct_child_offsets(table_view table) * | * i * - * The list parents are still needed to define the range of elements in the leaf that belong to the - * same row. + * Note that the `decompose_lists` flag should be specified as follow: + * - `true` when preprocessing a table for equality comparison. + * - `false` when preprocessing a table for lexicographic comparision, since we need to keep all + * lists columns intact to input into the next preprocessing step. * * @param table The table whose struct columns to decompose. - * @param decompose_lists Whether to decompose lists columns (preprocessing for equality comparator) - * or output them unchanged (preprocessing for lexicographic comparator) + * @param decompose_lists Whether to decompose lists columns * @param column_order The per-column order if using output with lexicographic comparison * @param null_precedence The per-column null precedence * @return A tuple containing a table with all struct columns decomposed, new corresponding column @@ -544,14 +545,8 @@ transform_lists_of_structs(column_view const& lhs, } // else == child is not STRUCT or LIST: just go to the end of this function, no transformation. } - // Any structs-of-lists should be decomposed into empty struct type `Struct<>` before being - // processed by this function. - else if (lhs.type().id() == type_id::STRUCT) { - CUDF_EXPECTS(std::all_of(lhs.child_begin(), - lhs.child_end(), - [](auto const& child) { return child.type().id() != type_id::LIST; }), - "Structs columns should be decomposed before reaching this function."); - } + // else if (lhs.type().id() == type_id::STRUCT): Any structs-of-lists should be decomposed into + // empty struct type `Struct<>` before being processed by this function so we do nothing here. // Passthrough: nothing changed. return {lhs, rhs_opt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; @@ -617,67 +612,15 @@ transform_lists_of_structs(table_view const& lhs, std::move(out_cols_rhs)}; } -/** - * @brief Check if there is any lists-of-structs column in the input table that has a floating-point - * column nested inside it. - * - * @param input The tables to check - * @return The check result - */ -bool lists_of_structs_have_floating_point(table_view const& input) -{ - // Check if any (nested) column is floating-point type. - std::function const has_nested_floating_point = [&](auto const& col) { - return col.type().id() == type_id::FLOAT32 || col.type().id() == type_id::FLOAT64 || - std::any_of(col.child_begin(), col.child_end(), has_nested_floating_point); - }; - - return std::any_of(input.begin(), input.end(), [&](auto const& col) { - // Any structs-of-lists should be decomposed into empty struct type `Struct<>` before being - // processed by this function. - if (col.type().id() == type_id::STRUCT) { - CUDF_EXPECTS( - std::all_of(col.child_begin(), - col.child_end(), - [](auto const& child) { return child.type().id() != type_id::LIST; }), - "Structs columns should be decomposed before reaching this function."); - } - - // We are looking for lists-of-structs. - if (col.type().id() != type_id::LIST) { return false; } - - auto const child = col.child(lists_column_view::child_column_index); - if (child.type().id() == type_id::STRUCT && - std::any_of(child.child_begin(), child.child_end(), has_nested_floating_point)) { - return true; - } - if (child.type().id() == type_id::LIST && - lists_of_structs_have_floating_point(table_view{{child}})) { - return true; - } - - // Found a lists column of some-type other than STRUCT or LIST. - return false; - }); -} - -uint64_t generate_random_id() -{ - auto gen = std::mt19937{std::random_device{}()}; - auto dist = std::uniform_int_distribution{}; - return dist(gen); -} - } // namespace std::shared_ptr preprocessed_table::create_preprocessed_table( table_view const& preprocessed_input, std::vector&& verticalized_col_depths, - std::vector>&& structs_transformed_columns, + std::vector>&& transformed_columns, host_span column_order, host_span null_precedence, - uint64_t preprocessed_id, - bool ranked_floating_point, + bool ranked_children, rmm::cuda_stream_view stream) { check_lex_compatibility(preprocessed_input); @@ -699,18 +642,16 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(d_depths), std::move(dremel_data), std::move(d_dremel_device_view), - std::move(structs_transformed_columns), - preprocessed_id, - ranked_floating_point)); + std::move(transformed_columns), + ranked_children)); } else { return std::shared_ptr( new preprocessed_table(std::move(d_t), std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), - std::move(structs_transformed_columns), - preprocessed_id, - ranked_floating_point)); + std::move(transformed_columns), + ranked_children)); } } @@ -724,18 +665,16 @@ std::shared_ptr preprocessed_table::create( decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); // Unused variables are generated for rhs table which is not available here. - [[maybe_unused]] auto [transformed_t, unused_0, structs_transformed_columns, unused_1] = + [[maybe_unused]] auto [transformed_t, unused_0, transformed_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, new_null_precedence, stream); - auto const ranked_floating_point = structs_transformed_columns.size() > 0 && - lists_of_structs_have_floating_point(decomposed_input); + auto const ranked_children = transformed_columns.size() > 0; return create_preprocessed_table(transformed_t, std::move(verticalized_col_depths), - std::move(structs_transformed_columns), + std::move(transformed_columns), new_column_order, new_null_precedence, - generate_random_id(), - ranked_floating_point, + ranked_children, stream); } @@ -760,38 +699,27 @@ preprocessed_table::create(table_view const& lhs, decompose_structs(rhs, false /*no decompose lists*/, column_order, null_precedence); // Transform any (nested) lists-of-structs column into lists-of-integers column. - auto [transformed_lhs, - transformed_rhs_opt, - structs_transformed_columns_lhs, - structs_transformed_columns_rhs] = + auto [transformed_lhs, transformed_rhs_opt, transformed_columns_lhs, transformed_columns_rhs] = transform_lists_of_structs(decomposed_lhs, decomposed_rhs, new_null_precedence_lhs, stream); // This should be the same for both lhs and rhs but not all the time, such as when one table - // has 0 rows so we check separately for each of them. - auto const ranked_floating_point_lhs = structs_transformed_columns_lhs.size() > 0 && - lists_of_structs_have_floating_point(decomposed_lhs); - auto const ranked_floating_point_rhs = structs_transformed_columns_rhs.size() > 0 && - lists_of_structs_have_floating_point(decomposed_rhs); - - // The same ID will be assigned to all preprocessed_table(s) so we can know later if they are - // compatible to compare against each other. - auto const preprocessed_id = generate_random_id(); + // has 0 rows while the other has >0 rows. So we check separately for each of them. + auto const ranked_children_lhs = transformed_columns_lhs.size() > 0; + auto const ranked_children_rhs = transformed_columns_rhs.size() > 0; return {create_preprocessed_table(transformed_lhs, std::move(verticalized_col_depths_lhs), - std::move(structs_transformed_columns_lhs), + std::move(transformed_columns_lhs), new_column_order_lhs, new_null_precedence_lhs, - preprocessed_id, - ranked_floating_point_lhs, + ranked_children_lhs, stream), create_preprocessed_table(transformed_rhs_opt.value(), std::move(verticalized_col_depths_rhs), - std::move(structs_transformed_columns_rhs), + std::move(transformed_columns_rhs), new_column_order_lhs, new_null_precedence_lhs, - preprocessed_id, - ranked_floating_point_rhs, + ranked_children_rhs, stream)}; } @@ -802,38 +730,33 @@ preprocessed_table::preprocessed_table( rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::vector>&& structs_transformed_columns, - uint64_t preprocessed_id, - bool ranked_floating_point) + std::vector>&& transformed_columns, + bool ranked_children) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), - _structs_transformed_columns(std::move(structs_transformed_columns)), - _preprocessed_id(preprocessed_id), - _ranked_floating_point(ranked_floating_point) + _transformed_columns(std::move(transformed_columns)), + _ranked_children(ranked_children) { } -preprocessed_table::preprocessed_table( - table_device_view_owner&& table, - rmm::device_uvector&& column_order, - rmm::device_uvector&& null_precedence, - rmm::device_uvector&& depths, - std::vector>&& structs_transformed_columns, - uint64_t preprocessed_id, - bool ranked_floating_point) +preprocessed_table::preprocessed_table(table_device_view_owner&& table, + rmm::device_uvector&& column_order, + rmm::device_uvector&& null_precedence, + rmm::device_uvector&& depths, + std::vector>&& transformed_columns, + bool ranked_children) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), _depths(std::move(depths)), _dremel_data{}, _dremel_device_views{}, - _structs_transformed_columns(std::move(structs_transformed_columns)), - _preprocessed_id(preprocessed_id), - _ranked_floating_point(ranked_floating_point) + _transformed_columns(std::move(transformed_columns)), + _ranked_children(ranked_children) { } From f82b3a7808b84a9bfc430f28f39fdefa792d76a0 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 10:02:33 -0700 Subject: [PATCH 126/140] Fix docs --- .../cudf/table/experimental/row_operators.cuh | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 0ab3edef453..bd11054bc95 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -682,9 +682,10 @@ struct preprocessed_table { * be passed to the constructor of `lexicographic::self_comparator` or * `lexicographic::two_table_comparator` to avoid preprocessing again. * - * Note that the output of this factory function cannot be used in `two_table_comparator` if the - * input table contains lists-of-structs having floating-point children column. In such cases, - * please use the overload `preprocessed_table::create(table_view const&, table_view const&,...)`. + * Note that the output of this factory function should not be used in `two_table_comparator` if + * the input table contains lists-of-structs. In such cases, please use the overload + * `preprocessed_table::create(table_view const&, table_view const&,...)` to preprocess both input + * tables at the same time. * * @param table The table to preprocess * @param column_order Optional, host array the same length as a row that indicates the desired @@ -693,8 +694,8 @@ struct preprocessed_table { * @param null_precedence Optional, an array having the same length as the number of columns in * the input tables that indicates how null values compare to all other. If it is empty, * the order `null_order::BEFORE` will be used for all columns. - * @param stream The stream to launch kernels and h->d copies on while preprocessing. - * @return A shared pointer to a preprocessed table. + * @param stream The stream to launch kernels and h->d copies on while preprocessing + * @return A shared pointer to a preprocessed table */ static std::shared_ptr create(table_view const& table, host_span column_order, @@ -719,7 +720,7 @@ struct preprocessed_table { * @param null_precedence Optional, an array having the same length as the number of columns in * the input tables that indicates how null values compare to all other. If it is empty, * the order `null_order::BEFORE` will be used for all columns. - * @param stream The stream to launch kernels and h->d copies on while preprocessing. + * @param stream The stream to launch kernels and h->d copies on while preprocessing * @return A pair of shared pointers to the preprocessed tables */ static std::pair, std::shared_ptr> create( @@ -750,6 +751,7 @@ struct preprocessed_table { * @param ranked_children Flag indicating if the input table was preprocessed to transform * any nested child column into an integer column using `cudf::rank` * @param stream The stream to launch kernels and h->d copies on while preprocessing + * @return A shared pointer to a preprocessed table */ static std::shared_ptr create_preprocessed_table( table_view const& preprocessed_input, @@ -781,8 +783,8 @@ struct preprocessed_table { * contain an empty `dremel_device_view`. As such, this uvector has as many elements as * there are columns in the table (unlike the `dremel_data` parameter, which is only as * long as the number of list columns). - * @param structs_transformed_columns Store the intermediate columns generated from transforming - * lists-of-structs columns into lists-of-integers columns using `cudf::rank()` + * @param transformed_columns Store the intermediate columns generated from transforming + * nested children columns into integers columns using `cudf::rank()` * @param ranked_children Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank` */ @@ -792,14 +794,14 @@ struct preprocessed_table { rmm::device_uvector&& depths, std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, - std::vector>&& structs_transformed_columns, + std::vector>&& transformed_columns, bool ranked_children); preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, - std::vector>&& structs_transformed_columns, + std::vector>&& transformed_columns, bool ranked_children); /** @@ -870,8 +872,8 @@ struct preprocessed_table { // integers columns using `cudf::rank()`, need to be kept alive. std::vector> _transformed_columns; - // Flag to record if the input table was preprocessed to transform any lists-of-structs column - // having at least one floating-point child (at any nested level) using `cudf::rank`. + // Flag to record if the input table was preprocessed to transform any nested children column(s) + // into integer column(s) using `cudf::rank`. bool const _ranked_children; }; @@ -944,9 +946,9 @@ class self_comparator { * @tparam PhysicalElementComparator A relational comparator functor that compares individual * values rather than logical elements, defaults to `NaN` aware relational comparator * that evaluates `NaN` as greater than all other values. - * @throw cudf::logic_error if the input table were preprocessed to transform any - * lists-of-structs column having floating-point children into lists-of-integers column, - * but `PhysicalElementComparator` is not `sorting_physical_element_comparator`. + * @throw cudf::logic_error if the input table was preprocessed to transform any nested children + * columns into integer columns but `PhysicalElementComparator` is not + * `sorting_physical_element_comparator`. * @param nullate Indicates if any input column contains nulls. * @param comparator Physical element relational comparison functor. * @return A binary callable object. @@ -1084,7 +1086,7 @@ class two_table_comparator { * * If the input tables have nested types such as lists-of-structs, * which will be transformed into lists-of-integers during preprocesing, - * the preprocessed_table(s) must be generated together using the factory function + * the preprocessed_table(s) must have been generated together using the factory function * `preprocessed_table::create(table_view const&, table_view const&)`. * * @param left A table preprocessed for lexicographic comparison @@ -1125,9 +1127,9 @@ class two_table_comparator { * @tparam PhysicalElementComparator A relational comparator functor that compares individual * values rather than logical elements, defaults to `NaN` aware relational comparator * that evaluates `NaN` as greater than all other values. - * @throw cudf::logic_error if the input tables were preprocessed to transform any - * lists-of-structs column having floating-point children into lists-of-integers column, - * but `PhysicalElementComparator` is not `sorting_physical_element_comparator`. + * @throw cudf::logic_error if the input tables were preprocessed to transform any nested children + * columns into integer columns but `PhysicalElementComparator` is not + * `sorting_physical_element_comparator`. * @param nullate Indicates if any input column contains nulls. * @param comparator Physical element relational comparison functor. * @return A binary callable object. From 1aed40de4ece12facf9d68abd7597b1a5008227b Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 10:16:38 -0700 Subject: [PATCH 127/140] Change comments --- cpp/src/table/row_operators.cu | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index f5acba5d1d7..c98e5a8c3b8 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -454,7 +454,7 @@ transform_lists_of_structs(column_view const& lhs, // (`order::ASCENDING`), we have `transformed_input = [ [0, 2], [0, 1] ]`. Sorting of // `transformed_input` will produce the same result as sorting `input` regardless of sorting // order (ASC or DESC). - auto const compute_ranks = [&](column_view const& input) { + auto const compute_ranks = [&](auto const& input) { return cudf::detail::rank(input, rank_method::DENSE, order::ASCENDING, @@ -543,10 +543,12 @@ transform_lists_of_structs(column_view const& lhs, } } } - // else == child is not STRUCT or LIST: just go to the end of this function, no transformation. + // else: child is not STRUCT or LIST: just go to the end of this function, no transformation. } - // else if (lhs.type().id() == type_id::STRUCT): Any structs-of-lists should be decomposed into - // empty struct type `Struct<>` before being processed by this function so we do nothing here. + // else: lhs.type().id() != type_id::LIST. + // In such situations, lhs.type().id() can still be type_id::STRUCT. However, any + // structs-of-lists should be decomposed into empty struct type `Struct<>` before being + // processed by this function so we do nothing here. // Passthrough: nothing changed. return {lhs, rhs_opt, std::move(out_cols_lhs), std::move(out_cols_rhs)}; @@ -566,8 +568,8 @@ transform_lists_of_structs(column_view const& lhs, * the order `null_order::BEFORE` will be used for all columns. * @param stream CUDA stream used for device memory operations and kernel launches * @return A tuple consisting of new table_view representing the transformed input, along with - * the ranks column (of `size_type` type) and possibly new list offsets generated during the - * transformation process + * the ranks columns (of `size_type` type) and possibly new list offsets generated during + * the transformation process */ std::tuple, @@ -595,8 +597,8 @@ transform_lists_of_structs(table_view const& lhs, null_precedence.empty() ? null_order::BEFORE : null_precedence[col_idx], stream); - transformed_lhs_cvs.push_back(transformed_lhs); - if (rhs) { transformed_rhs_cvs.push_back(transformed_rhs_opt.value()); } + transformed_lhs_cvs.emplace_back(std::move(transformed_lhs)); + if (rhs) { transformed_rhs_cvs.emplace_back(std::move(transformed_rhs_opt.value())); } out_cols_lhs.insert(out_cols_lhs.end(), std::make_move_iterator(curr_out_cols_lhs.begin()), From f0f4b894a524dfa80b1a1f3aa7edb6db555cc679 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 10:31:02 -0700 Subject: [PATCH 128/140] Rename variable --- cpp/src/table/row_operators.cu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index c98e5a8c3b8..85f09a46818 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -627,7 +627,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl { check_lex_compatibility(preprocessed_input); - auto d_t = table_device_view::create(preprocessed_input, stream); + auto d_table = table_device_view::create(preprocessed_input, stream); auto d_column_order = detail::make_device_uvector_async(column_order, stream, rmm::mr::get_current_device_resource()); auto d_null_precedence = detail::make_device_uvector_async( @@ -638,7 +638,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl if (detail::has_nested_columns(preprocessed_input)) { auto [dremel_data, d_dremel_device_view] = list_lex_preprocess(preprocessed_input, stream); return std::shared_ptr( - new preprocessed_table(std::move(d_t), + new preprocessed_table(std::move(d_table), std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), @@ -648,7 +648,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl ranked_children)); } else { return std::shared_ptr( - new preprocessed_table(std::move(d_t), + new preprocessed_table(std::move(d_table), std::move(d_column_order), std::move(d_null_precedence), std::move(d_depths), @@ -667,11 +667,11 @@ std::shared_ptr preprocessed_table::create( decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); // Unused variables are generated for rhs table which is not available here. - [[maybe_unused]] auto [transformed_t, unused_0, transformed_columns, unused_1] = + [[maybe_unused]] auto [transformed_input, unused_0, transformed_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, new_null_precedence, stream); auto const ranked_children = transformed_columns.size() > 0; - return create_preprocessed_table(transformed_t, + return create_preprocessed_table(transformed_input, std::move(verticalized_col_depths), std::move(transformed_columns), new_column_order, From 2f406a303b2bd1f5975392aab8c9f3a5f7a83154 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 10:31:05 -0700 Subject: [PATCH 129/140] Fix tests --- .../table/experimental_row_operator_tests.cu | 51 ++++--------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/cpp/tests/table/experimental_row_operator_tests.cu b/cpp/tests/table/experimental_row_operator_tests.cu index ffa2b9f2b80..ed97f6b6d35 100644 --- a/cpp/tests/table/experimental_row_operator_tests.cu +++ b/cpp/tests/table/experimental_row_operator_tests.cu @@ -373,27 +373,16 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, expected_rhs); - if constexpr (std::is_floating_point_v) { - EXPECT_THROW(test_sort(preprocessed_lhs, - lhs, - cudf::experimental::row::lexicographic::physical_element_comparator{}, - expected_lhs), - cudf::logic_error); - EXPECT_THROW(test_sort(preprocessed_rhs, - rhs, - cudf::experimental::row::lexicographic::physical_element_comparator{}, - expected_rhs), - cudf::logic_error); - } else { - test_sort(preprocessed_lhs, - lhs, - cudf::experimental::row::lexicographic::physical_element_comparator{}, - expected_lhs); - test_sort(preprocessed_rhs, - rhs, - cudf::experimental::row::lexicographic::physical_element_comparator{}, - expected_rhs); - } + EXPECT_THROW(test_sort(preprocessed_lhs, + lhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_lhs), + cudf::logic_error); + EXPECT_THROW(test_sort(preprocessed_rhs, + rhs, + cudf::experimental::row::lexicographic::physical_element_comparator{}, + expected_rhs), + cudf::logic_error); }; // Generate preprocessed data for both lhs and lhs at the same time. @@ -411,26 +400,6 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) rhs /*empty*/, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); } - - // Test creating two_table_comparator using preprocessed_table(s). - { - auto const [preprocessed_lhs, preprocessed_rhs] = - cudf::experimental::row::lexicographic::preprocessed_table::create( - lhs, rhs, std::vector{cudf::order::ASCENDING}, {}, stream); - EXPECT_NO_THROW(cudf::experimental::row::lexicographic::two_table_comparator(preprocessed_lhs, - preprocessed_rhs)); - } - { - auto const preprocessed_lhs = - cudf::experimental::row::lexicographic::preprocessed_table::create( - lhs, std::vector{cudf::order::ASCENDING}, {}, stream); - auto const preprocessed_rhs = - cudf::experimental::row::lexicographic::preprocessed_table::create( - rhs, std::vector{cudf::order::ASCENDING}, {}, stream); - EXPECT_THROW(cudf::experimental::row::lexicographic::two_table_comparator(preprocessed_lhs, - preprocessed_rhs), - cudf::logic_error); - } } template From e07a54ef3064406ee554f34e74449ab109f60c17 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 10:33:52 -0700 Subject: [PATCH 130/140] Rename function --- cpp/tests/table/experimental_row_operator_tests.cu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/tests/table/experimental_row_operator_tests.cu b/cpp/tests/table/experimental_row_operator_tests.cu index ed97f6b6d35..9e305ffeabd 100644 --- a/cpp/tests/table/experimental_row_operator_tests.cu +++ b/cpp/tests/table/experimental_row_operator_tests.cu @@ -188,7 +188,7 @@ auto two_table_equality(cudf::table_view lhs, } template -auto sort_table( +auto sorted_order( std::shared_ptr preprocessed_input, cudf::size_type num_rows, bool has_nested, @@ -275,9 +275,9 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTables) auto const& input, auto const& comparator, auto const& expected) { - auto const output = sort_table( + auto const order = sorted_order( preprocessed, input.num_rows(), cudf::detail::has_nested_columns(input), comparator, stream); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, output->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, order->view()); }; auto const test_sort_two_tables = [&](auto const& preprocessed_lhs, @@ -354,9 +354,9 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) auto const& input, auto const& comparator, auto const& expected) { - auto const output = sort_table( + auto const order = sorted_order( preprocessed, input.num_rows(), cudf::detail::has_nested_columns(input), comparator, stream); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, output->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, order->view()); }; auto const test_sort_two_tables = [&](auto const& preprocessed_lhs, From 1c37a17133f794950ce8d205efc17f49948045e5 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 11:15:50 -0700 Subject: [PATCH 131/140] Fix spell --- cpp/src/table/row_operators.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 85f09a46818..7184d4bdb9b 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -164,7 +164,7 @@ table_view remove_struct_child_offsets(table_view table) * * Note that the `decompose_lists` flag should be specified as follow: * - `true` when preprocessing a table for equality comparison. - * - `false` when preprocessing a table for lexicographic comparision, since we need to keep all + * - `false` when preprocessing a table for lexicographic comparison, since we need to keep all * lists columns intact to input into the next preprocessing step. * * @param table The table whose struct columns to decompose. From dd94820b7d5d5ddfb13d2fcbe57a7371410bc97b Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Fri, 21 Apr 2023 11:22:17 -0700 Subject: [PATCH 132/140] Fix spell --- cpp/include/cudf/table/experimental/row_operators.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index bd11054bc95..59cf87a5a76 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -1085,7 +1085,7 @@ class two_table_comparator { * multiple comparators. * * If the input tables have nested types such as lists-of-structs, - * which will be transformed into lists-of-integers during preprocesing, + * which will be transformed into lists-of-integers during preprocessing, * the preprocessed_table(s) must have been generated together using the factory function * `preprocessed_table::create(table_view const&, table_view const&)`. * From 3e4b7b2e22a332cef7b4473a69c65333b192f210 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 25 Apr 2023 09:09:32 -0700 Subject: [PATCH 133/140] Rename `*ranked_children` into `*has_ranked_children` --- .../cudf/table/experimental/row_operators.cuh | 20 +++++++------- cpp/src/table/row_operators.cu | 26 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 59cf87a5a76..bb142001c4a 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -748,7 +748,7 @@ struct preprocessed_table { * @param null_precedence Optional, an array having the same length as the number of columns in * the input tables that indicates how null values compare to all other. If it is empty, * the order `null_order::BEFORE` will be used for all columns. - * @param ranked_children Flag indicating if the input table was preprocessed to transform + * @param has_ranked_children Flag indicating if the input table was preprocessed to transform * any nested child column into an integer column using `cudf::rank` * @param stream The stream to launch kernels and h->d copies on while preprocessing * @return A shared pointer to a preprocessed table @@ -759,7 +759,7 @@ struct preprocessed_table { std::vector>&& transformed_columns, host_span column_order, host_span null_precedence, - bool ranked_children, + bool has_ranked_children, rmm::cuda_stream_view stream); /** @@ -785,7 +785,7 @@ struct preprocessed_table { * long as the number of list columns). * @param transformed_columns Store the intermediate columns generated from transforming * nested children columns into integers columns using `cudf::rank()` - * @param ranked_children Flag indicating if the input table was preprocessed to transform + * @param has_ranked_children Flag indicating if the input table was preprocessed to transform * any lists-of-structs column having floating-point children using `cudf::rank` */ preprocessed_table(table_device_view_owner&& table, @@ -795,14 +795,14 @@ struct preprocessed_table { std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::vector>&& transformed_columns, - bool ranked_children); + bool has_ranked_children); preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& column_order, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::vector>&& transformed_columns, - bool ranked_children); + bool has_ranked_children); /** * @brief Implicit conversion operator to a `table_device_view` of the preprocessed table. @@ -874,7 +874,7 @@ struct preprocessed_table { // Flag to record if the input table was preprocessed to transform any nested children column(s) // into integer column(s) using `cudf::rank`. - bool const _ranked_children; + bool const _has_ranked_children; }; /** @@ -959,7 +959,7 @@ class self_comparator { auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_ranked_children, + CUDF_EXPECTS(!d_t->_has_ranked_children, "The input table has nested type children and they were transformed using a " "different type of physical element comparator."); } @@ -984,7 +984,7 @@ class self_comparator { auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_ranked_children, + CUDF_EXPECTS(!d_t->_has_ranked_children, "The input table has nested type children and they were transformed using a " "different type of physical element comparator."); } @@ -1140,7 +1140,7 @@ class two_table_comparator { auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_left_table->_ranked_children && !d_right_table->_ranked_children, + CUDF_EXPECTS(!d_left_table->_has_ranked_children && !d_right_table->_has_ranked_children, "The input tables have nested type children and they were transformed using a " "different type of physical element comparator."); } @@ -1165,7 +1165,7 @@ class two_table_comparator { auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_left_table->_ranked_children && !d_right_table->_ranked_children, + CUDF_EXPECTS(!d_left_table->_has_ranked_children && !d_right_table->_has_ranked_children, "The input tables have nested type children and they were transformed using a " "different type of physical element comparator."); } diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 7184d4bdb9b..6692dd109b7 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -622,7 +622,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::vector>&& transformed_columns, host_span column_order, host_span null_precedence, - bool ranked_children, + bool has_ranked_children, rmm::cuda_stream_view stream) { check_lex_compatibility(preprocessed_input); @@ -645,7 +645,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(dremel_data), std::move(d_dremel_device_view), std::move(transformed_columns), - ranked_children)); + has_ranked_children)); } else { return std::shared_ptr( new preprocessed_table(std::move(d_table), @@ -653,7 +653,7 @@ std::shared_ptr preprocessed_table::create_preprocessed_tabl std::move(d_null_precedence), std::move(d_depths), std::move(transformed_columns), - ranked_children)); + has_ranked_children)); } } @@ -670,13 +670,13 @@ std::shared_ptr preprocessed_table::create( [[maybe_unused]] auto [transformed_input, unused_0, transformed_columns, unused_1] = transform_lists_of_structs(decomposed_input, std::nullopt, new_null_precedence, stream); - auto const ranked_children = transformed_columns.size() > 0; + auto const has_ranked_children = !transformed_columns.empty(); return create_preprocessed_table(transformed_input, std::move(verticalized_col_depths), std::move(transformed_columns), new_column_order, new_null_precedence, - ranked_children, + has_ranked_children, stream); } @@ -706,22 +706,22 @@ preprocessed_table::create(table_view const& lhs, // This should be the same for both lhs and rhs but not all the time, such as when one table // has 0 rows while the other has >0 rows. So we check separately for each of them. - auto const ranked_children_lhs = transformed_columns_lhs.size() > 0; - auto const ranked_children_rhs = transformed_columns_rhs.size() > 0; + auto const has_ranked_children_lhs = !transformed_columns_lhs.empty(); + auto const has_ranked_children_rhs = !transformed_columns_rhs.empty(); return {create_preprocessed_table(transformed_lhs, std::move(verticalized_col_depths_lhs), std::move(transformed_columns_lhs), new_column_order_lhs, new_null_precedence_lhs, - ranked_children_lhs, + has_ranked_children_lhs, stream), create_preprocessed_table(transformed_rhs_opt.value(), std::move(verticalized_col_depths_rhs), std::move(transformed_columns_rhs), new_column_order_lhs, new_null_precedence_lhs, - ranked_children_rhs, + has_ranked_children_rhs, stream)}; } @@ -733,7 +733,7 @@ preprocessed_table::preprocessed_table( std::vector&& dremel_data, rmm::device_uvector&& dremel_device_views, std::vector>&& transformed_columns, - bool ranked_children) + bool has_ranked_children) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -741,7 +741,7 @@ preprocessed_table::preprocessed_table( _dremel_data(std::move(dremel_data)), _dremel_device_views(std::move(dremel_device_views)), _transformed_columns(std::move(transformed_columns)), - _ranked_children(ranked_children) + _has_ranked_children(has_ranked_children) { } @@ -750,7 +750,7 @@ preprocessed_table::preprocessed_table(table_device_view_owner&& table, rmm::device_uvector&& null_precedence, rmm::device_uvector&& depths, std::vector>&& transformed_columns, - bool ranked_children) + bool has_ranked_children) : _t(std::move(table)), _column_order(std::move(column_order)), _null_precedence(std::move(null_precedence)), @@ -758,7 +758,7 @@ preprocessed_table::preprocessed_table(table_device_view_owner&& table, _dremel_data{}, _dremel_device_views{}, _transformed_columns(std::move(transformed_columns)), - _ranked_children(ranked_children) + _has_ranked_children(has_ranked_children) { } From eb73c754c519fd217df85d242fc31dee4457cd26 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 25 Apr 2023 19:56:11 -0700 Subject: [PATCH 134/140] Use enum `decompose_lists_column` instead of boolean value --- cpp/src/table/row_operators.cu | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 6692dd109b7..d4b280ee6cb 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -87,6 +87,12 @@ table_view remove_struct_child_offsets(table_view table) return table_view(cols); } +/** + * @brief The enum to specify whether the `decompose_structs` function will process lists columns + * (at any nested level) or will output them unchanged. + */ +enum class decompose_lists_column : bool { YES, NO }; + /** * @brief Decompose all struct columns in a table * @@ -175,7 +181,7 @@ table_view remove_struct_child_offsets(table_view table) * orders and null precedences and depths of the linearized branches */ auto decompose_structs(table_view table, - bool decompose_lists, + decompose_lists_column decompose_lists, host_span column_order = {}, host_span null_precedence = {}) { @@ -196,7 +202,7 @@ auto decompose_structs(table_view table, std::vector* branch, int depth) { branch->push_back(c); - if (decompose_lists && c->type().id() == type_id::LIST) { + if (decompose_lists == decompose_lists_column::YES && c->type().id() == type_id::LIST) { recursive_child( c->children[lists_column_view::child_column_index].get(), branch, depth + 1); } else if (c->type().id() == type_id::STRUCT) { @@ -664,7 +670,7 @@ std::shared_ptr preprocessed_table::create( rmm::cuda_stream_view stream) { auto [decomposed_input, new_column_order, new_null_precedence, verticalized_col_depths] = - decompose_structs(input, false /*no decompose lists*/, column_order, null_precedence); + decompose_structs(input, decompose_lists_column::NO, column_order, null_precedence); // Unused variables are generated for rhs table which is not available here. [[maybe_unused]] auto [transformed_input, unused_0, transformed_columns, unused_1] = @@ -693,12 +699,12 @@ preprocessed_table::create(table_view const& lhs, new_column_order_lhs, new_null_precedence_lhs, verticalized_col_depths_lhs] = - decompose_structs(lhs, false /*no decompose lists*/, column_order, null_precedence); + decompose_structs(lhs, decompose_lists_column::NO, column_order, null_precedence); // Unused variables are new column order and null order for rhs, which are the same as for lhs // so we don't need them. [[maybe_unused]] auto [decomposed_rhs, unused0, unused1, verticalized_col_depths_rhs] = - decompose_structs(rhs, false /*no decompose lists*/, column_order, null_precedence); + decompose_structs(rhs, decompose_lists_column::NO, column_order, null_precedence); // Transform any (nested) lists-of-structs column into lists-of-integers column. auto [transformed_lhs, transformed_rhs_opt, transformed_columns_lhs, transformed_columns_rhs] = @@ -785,7 +791,7 @@ std::shared_ptr preprocessed_table::create(table_view const& structs::detail::push_down_nulls(t, stream, rmm::mr::get_current_device_resource()); auto struct_offset_removed_table = remove_struct_child_offsets(null_pushed_table); auto verticalized_t = - std::get<0>(decompose_structs(struct_offset_removed_table, true /*decompose lists*/)); + std::get<0>(decompose_structs(struct_offset_removed_table, decompose_lists_column::YES)); auto d_t = table_device_view_owner(table_device_view::create(verticalized_t, stream)); return std::shared_ptr(new preprocessed_table( From 3a12c2d1a2cfdad3b5913971f243da61e8a4e337 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 25 Apr 2023 20:04:00 -0700 Subject: [PATCH 135/140] Add comment --- cpp/include/cudf/table/experimental/row_operators.cuh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index bb142001c4a..60a12a0ad21 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -1084,10 +1084,9 @@ class two_table_comparator { * This constructor allows independently constructing a `preprocessed_table` and sharing it among * multiple comparators. * - * If the input tables have nested types such as lists-of-structs, - * which will be transformed into lists-of-integers during preprocessing, - * the preprocessed_table(s) must have been generated together using the factory function - * `preprocessed_table::create(table_view const&, table_view const&)`. + * The preprocessed_table(s) should have been pre-generated together using the factory function + * `preprocessed_table::create(table_view const&, table_view const&)`. Otherwise, the comparison + * results between two tables may be incorrect. * * @param left A table preprocessed for lexicographic comparison * @param right A table preprocessed for lexicographic comparison From 3f38b8242703bacbaa0ed9970c7f9b76cf44041d Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 25 Apr 2023 20:10:19 -0700 Subject: [PATCH 136/140] Fix docs --- cpp/src/table/row_operators.cu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index d4b280ee6cb..2401b33ffd5 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -169,9 +169,9 @@ enum class decompose_lists_column : bool { YES, NO }; * i * * Note that the `decompose_lists` flag should be specified as follow: - * - `true` when preprocessing a table for equality comparison. - * - `false` when preprocessing a table for lexicographic comparison, since we need to keep all - * lists columns intact to input into the next preprocessing step. + * - `decompose_lists_column::YES` when preprocessing a table for equality comparison. + * - `decompose_lists_column::NO` when preprocessing a table for lexicographic comparison, + * since we need to keep all lists columns intact to input into the next preprocessing step. * * @param table The table whose struct columns to decompose. * @param decompose_lists Whether to decompose lists columns From a44d75a7e5fbda6cd444d87f55af8094559e629a Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 2 May 2023 20:42:38 -0700 Subject: [PATCH 137/140] Move `constexpr` order --- cpp/benchmarks/sort/sort_lists.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/benchmarks/sort/sort_lists.cpp b/cpp/benchmarks/sort/sort_lists.cpp index 01523423e10..3cab60a29ec 100644 --- a/cpp/benchmarks/sort/sort_lists.cpp +++ b/cpp/benchmarks/sort/sort_lists.cpp @@ -21,8 +21,8 @@ #include namespace { -cudf::size_type constexpr min_val = 0; -cudf::size_type constexpr max_val = 100; +constexpr cudf::size_type min_val = 0; +constexpr cudf::size_type max_val = 100; void sort_multiple_lists(nvbench::state& state) { From 4fd2ab5d516bd9786eb47011847bc779a1773caf Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 2 May 2023 20:42:58 -0700 Subject: [PATCH 138/140] Rename fuction and extract `check_physical_element_comparator` --- .../cudf/table/experimental/row_operators.cuh | 38 +++++++--------- cpp/src/table/row_operators.cu | 44 +++++++++---------- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/cpp/include/cudf/table/experimental/row_operators.cuh b/cpp/include/cudf/table/experimental/row_operators.cuh index 60a12a0ad21..3e37bd53972 100644 --- a/cpp/include/cudf/table/experimental/row_operators.cuh +++ b/cpp/include/cudf/table/experimental/row_operators.cuh @@ -753,7 +753,7 @@ struct preprocessed_table { * @param stream The stream to launch kernels and h->d copies on while preprocessing * @return A shared pointer to a preprocessed table */ - static std::shared_ptr create_preprocessed_table( + static std::shared_ptr create( table_view const& preprocessed_input, std::vector&& verticalized_col_depths, std::vector>&& transformed_columns, @@ -858,6 +858,16 @@ struct preprocessed_table { } } + template + void check_physical_element_comparator() + { + if constexpr (!std::is_same_v) { + CUDF_EXPECTS(!_has_ranked_children, + "The input table has nested type children and they were transformed using a " + "different type of physical element comparator."); + } + } + private: table_device_view_owner const _t; rmm::device_uvector const _column_order; @@ -958,11 +968,7 @@ class self_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_has_ranked_children, - "The input table has nested type children and they were transformed using a " - "different type of physical element comparator."); - } + d_t->check_physical_element_comparator(); return less_comparator{ device_row_comparator{ @@ -983,11 +989,7 @@ class self_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_t->_has_ranked_children, - "The input table has nested type children and they were transformed using a " - "different type of physical element comparator."); - } + d_t->check_physical_element_comparator(); return less_equivalent_comparator{ device_row_comparator{ @@ -1138,11 +1140,8 @@ class two_table_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_left_table->_has_ranked_children && !d_right_table->_has_ranked_children, - "The input tables have nested type children and they were transformed using a " - "different type of physical element comparator."); - } + d_left_table->check_physical_element_comparator(); + d_right_table->check_physical_element_comparator(); return less_comparator{strong_index_comparator_adapter{ device_row_comparator{ @@ -1163,11 +1162,8 @@ class two_table_comparator { typename PhysicalElementComparator = sorting_physical_element_comparator> auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const { - if constexpr (!std::is_same_v) { - CUDF_EXPECTS(!d_left_table->_has_ranked_children && !d_right_table->_has_ranked_children, - "The input tables have nested type children and they were transformed using a " - "different type of physical element comparator."); - } + d_left_table->check_physical_element_comparator(); + d_right_table->check_physical_element_comparator(); return less_equivalent_comparator{strong_index_comparator_adapter{ device_row_comparator{ diff --git a/cpp/src/table/row_operators.cu b/cpp/src/table/row_operators.cu index 2401b33ffd5..9f3a5bcdfea 100644 --- a/cpp/src/table/row_operators.cu +++ b/cpp/src/table/row_operators.cu @@ -622,7 +622,7 @@ transform_lists_of_structs(table_view const& lhs, } // namespace -std::shared_ptr preprocessed_table::create_preprocessed_table( +std::shared_ptr preprocessed_table::create( table_view const& preprocessed_input, std::vector&& verticalized_col_depths, std::vector>&& transformed_columns, @@ -677,13 +677,13 @@ std::shared_ptr preprocessed_table::create( transform_lists_of_structs(decomposed_input, std::nullopt, new_null_precedence, stream); auto const has_ranked_children = !transformed_columns.empty(); - return create_preprocessed_table(transformed_input, - std::move(verticalized_col_depths), - std::move(transformed_columns), - new_column_order, - new_null_precedence, - has_ranked_children, - stream); + return create(transformed_input, + std::move(verticalized_col_depths), + std::move(transformed_columns), + new_column_order, + new_null_precedence, + has_ranked_children, + stream); } std::pair, std::shared_ptr> @@ -715,20 +715,20 @@ preprocessed_table::create(table_view const& lhs, auto const has_ranked_children_lhs = !transformed_columns_lhs.empty(); auto const has_ranked_children_rhs = !transformed_columns_rhs.empty(); - return {create_preprocessed_table(transformed_lhs, - std::move(verticalized_col_depths_lhs), - std::move(transformed_columns_lhs), - new_column_order_lhs, - new_null_precedence_lhs, - has_ranked_children_lhs, - stream), - create_preprocessed_table(transformed_rhs_opt.value(), - std::move(verticalized_col_depths_rhs), - std::move(transformed_columns_rhs), - new_column_order_lhs, - new_null_precedence_lhs, - has_ranked_children_rhs, - stream)}; + return {create(transformed_lhs, + std::move(verticalized_col_depths_lhs), + std::move(transformed_columns_lhs), + new_column_order_lhs, + new_null_precedence_lhs, + has_ranked_children_lhs, + stream), + create(transformed_rhs_opt.value(), + std::move(verticalized_col_depths_rhs), + std::move(transformed_columns_rhs), + new_column_order_lhs, + new_null_precedence_lhs, + has_ranked_children_rhs, + stream)}; } preprocessed_table::preprocessed_table( From f0b713d25319fb576dae2bc6d70c65cc656ce925 Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 2 May 2023 21:02:44 -0700 Subject: [PATCH 139/140] Change unit tests --- cpp/tests/search/search_list_test.cpp | 76 +++++++++---------- cpp/tests/sort/sort_nested_types_tests.cpp | 7 +- .../table/experimental_row_operator_tests.cu | 66 ++++++++-------- 3 files changed, 72 insertions(+), 77 deletions(-) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index ccf0cc9b80e..cc42c467e90 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -34,9 +34,8 @@ using structs_col = cudf::test::structs_column_wrapper; using strings_col = cudf::test::strings_column_wrapper; constexpr cudf::test::debug_output_level verbosity{cudf::test::debug_output_level::FIRST_ERROR}; -constexpr int32_t null{0}; // Mark for null child elements at the current level -constexpr int32_t XXX{0}; // Mark for null elements at all levels -constexpr int32_t dont_care{0}; // Mark for elements that will be sliced off +constexpr int32_t null{0}; // Mark for null child elements at the current level +constexpr int32_t XXX{0}; // Mark for null elements at all levels using TestTypes = cudf::test::Concat; using lists_col = cudf::test::lists_column_wrapper; - auto const haystack_original = - lists_col{{dont_care, dont_care}, {dont_care}, {1, 2}, {1}, {}, {1, 3}, {dont_care, dont_care}}; - auto const haystack = cudf::slice(haystack_original, {2, 6})[0]; + auto const haystack_original = lists_col{{0, 0}, {0}, {1, 2}, {1}, {}, {1, 3}, {0, 0}}; + auto const haystack = cudf::slice(haystack_original, {2, 6})[0]; auto const needle1 = [] { auto child = tdata_col{1, 2}; @@ -107,7 +105,7 @@ TYPED_TEST(TypedListsContainsTestScalarNeedle, SlicedColumnInput) return cudf::list_scalar(child); }(); auto const needle3 = [] { - auto child = tdata_col{dont_care, dont_care}; + auto child = tdata_col{0, 0}; return cudf::list_scalar(child); }(); @@ -187,8 +185,8 @@ TYPED_TEST(TypedListsContainsTestScalarNeedle, SlicedInputHavingNulls) using tdata_col = cudf::test::fixed_width_column_wrapper; using lists_col = cudf::test::lists_column_wrapper; - auto const haystack_original = lists_col{{{dont_care, dont_care}, - {dont_care} /*NULL*/, + auto const haystack_original = lists_col{{{0, 0}, + {0} /*NULL*/, lists_col{{1, null}, null_at(1)}, {1}, {} /*NULL*/, @@ -196,7 +194,7 @@ TYPED_TEST(TypedListsContainsTestScalarNeedle, SlicedInputHavingNulls) {4}, {} /*NULL*/, {1, 1}, - {dont_care}}, + {0}}, nulls_at({1, 4, 7})}; auto const haystack = cudf::slice(haystack_original, {2, 9})[0]; @@ -209,7 +207,7 @@ TYPED_TEST(TypedListsContainsTestScalarNeedle, SlicedInputHavingNulls) return cudf::list_scalar(child); }(); auto const needle3 = [] { - auto child = tdata_col{dont_care, dont_care}; + auto child = tdata_col{0, 0}; return cudf::list_scalar(child); }(); @@ -250,13 +248,12 @@ TYPED_TEST(TypedListContainsTestColumnNeedles, SlicedInputNoNulls) { using lists_col = cudf::test::lists_column_wrapper; - auto const haystack_original = lists_col{ - {dont_care, dont_care}, {dont_care}, {0, 1}, {2}, {3, 4, 5}, {2, 3, 4}, {}, {0, 2, 0}}; + auto const haystack_original = + lists_col{{0, 0}, {0}, {0, 1}, {2}, {3, 4, 5}, {2, 3, 4}, {}, {0, 2, 0}}; auto const haystack = cudf::slice(haystack_original, {2, 8})[0]; - auto const needles_original = - lists_col{{dont_care}, {0, 1}, {0, 0}, {3, 5, 4}, {}, {dont_care, dont_care}, {} /*dont_care*/}; - auto const needles = cudf::slice(needles_original, {1, 5})[0]; + auto const needles_original = lists_col{{0}, {0, 1}, {0, 0}, {3, 5, 4}, {}, {0, 0}, {} /*0*/}; + auto const needles = cudf::slice(needles_original, {1, 5})[0]; auto const expected = bools_col{1, 0, 0, 1}; auto const result = cudf::contains(haystack, needles); @@ -267,8 +264,8 @@ TYPED_TEST(TypedListContainsTestColumnNeedles, SlicedInputHavingNulls) { using lists_col = cudf::test::lists_column_wrapper; - auto const haystack_original = lists_col{{{dont_care, dont_care}, - {dont_care} /*NULL*/, + auto const haystack_original = lists_col{{{0, 0}, + {0} /*NULL*/, lists_col{{1, null}, null_at(1)}, {1}, {} /*NULL*/, @@ -276,12 +273,12 @@ TYPED_TEST(TypedListContainsTestColumnNeedles, SlicedInputHavingNulls) {4}, {} /*NULL*/, {1, 1}, - {dont_care}}, + {0}}, nulls_at({1, 4, 7})}; auto const haystack = cudf::slice(haystack_original, {2, 9})[0]; - auto const needles_original = lists_col{{{dont_care, dont_care}, - {dont_care} /*NULL*/, + auto const needles_original = lists_col{{{0, 0}, + {0} /*NULL*/, lists_col{{1, null}, null_at(1)}, {1}, {} /*NULL*/, @@ -289,7 +286,7 @@ TYPED_TEST(TypedListContainsTestColumnNeedles, SlicedInputHavingNulls) {4}, {} /*NULL*/, {}, - {dont_care}}, + {0}}, nulls_at({1, 4, 7})}; auto const needles = cudf::slice(needles_original, {2, 9})[0]; @@ -374,13 +371,14 @@ TEST_F(ListBinarySearch, ListWithNulls) lcw{{null, 4.22671e+32}, null_at(0)}, }; - auto const expect = int32s_col{0}; - auto const results = search_bounds(cudf::table_view{{haystack}}, - cudf::table_view{{needles}}, - {cudf::order::ASCENDING}, - {cudf::null_order::BEFORE}); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expect, results.first->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expect, results.second->view()); + auto const expected = int32s_col{0}; + auto const [result_lower_bound, result_upper_bound] = + search_bounds(cudf::table_view{{haystack}}, + cudf::table_view{{needles}}, + {cudf::order::ASCENDING}, + {cudf::null_order::BEFORE}); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, *result_lower_bound, verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, *result_upper_bound, verbosity); } { @@ -411,11 +409,11 @@ TEST_F(ListBinarySearch, ListWithNulls) std::vector null_order_flags{cudf::null_order::BEFORE, cudf::null_order::BEFORE}; - auto const expected = int32s_col{3}; - auto const results = search_bounds( + auto const expected = int32s_col{3}; + auto const [result_lower_bound, result_upper_bound] = search_bounds( cudf::table_view{{input}}, cudf::table_view{{values}}, column_order, null_order_flags); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, results.first->view()); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, results.second->view()); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, *result_lower_bound, verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected, *result_upper_bound, verbosity); } } @@ -476,12 +474,12 @@ TEST_F(ListBinarySearch, ListsOfStructs) return cudf::make_lists_column(8, offsets.release(), child.release(), 0, {}); }(); - auto const results = search_bounds( + auto const [result_lower_bound, result_upper_bound] = search_bounds( cudf::table_view{{*haystack}}, cudf::table_view{{*needles}}, {cudf::order::ASCENDING}); auto const expected_lower_bound = int32s_col{1, 1, 4, 0, 0, 0, 1, 1}; auto const expected_upper_bound = int32s_col{1, 4, 4, 0, 0, 0, 4, 4}; - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), verbosity); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, *result_lower_bound, verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, *result_upper_bound, verbosity); } TEST_F(ListBinarySearch, ListsOfEqualStructsInTwoTables) @@ -546,10 +544,10 @@ TEST_F(ListBinarySearch, ListsOfEqualStructsInTwoTables) // In this search, the two table have many equal structs. // This help to verify the internal implementation of two-table lex comparator in which the // structs column of two input tables are concatenated, ranked, then split. - auto const results = search_bounds( + auto const [result_lower_bound, result_upper_bound] = search_bounds( cudf::table_view{{*haystack}}, cudf::table_view{{*needles}}, {cudf::order::ASCENDING}); auto const expected_lower_bound = int32s_col{0, 1, 4, 9, 4, 9, 5, 4, 8}; auto const expected_upper_bound = int32s_col{1, 4, 4, 9, 5, 9, 8, 4, 9}; - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, results.first->view(), verbosity); - CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, results.second->view(), verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, *result_lower_bound, verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, *result_upper_bound, verbosity); } diff --git a/cpp/tests/sort/sort_nested_types_tests.cpp b/cpp/tests/sort/sort_nested_types_tests.cpp index c02b576a201..ff6256c2408 100644 --- a/cpp/tests/sort/sort_nested_types_tests.cpp +++ b/cpp/tests/sort/sort_nested_types_tests.cpp @@ -307,13 +307,10 @@ TEST_F(NestedListTest, SimpleListsOfStructsNoNulls) TEST_F(NestedListTest, SlicedListsOfStructsNoNulls) { - auto constexpr dont_care{0}; auto const input_original = [] { auto const get_structs = [] { - auto child0 = - int32s_col{dont_care, dont_care, 3, 2, 3, 3, 4, 2, 4, 4, 1, 0, 3, 0, 2, 5, 4, dont_care}; - auto child1 = - int32s_col{dont_care, dont_care, 0, 4, 3, 2, 1, 1, 5, 1, 5, 5, 4, 2, 4, 1, 3, dont_care}; + auto child0 = int32s_col{0, 0, 3, 2, 3, 3, 4, 2, 4, 4, 1, 0, 3, 0, 2, 5, 4, 0}; + auto child1 = int32s_col{0, 0, 0, 4, 3, 2, 1, 1, 5, 1, 5, 5, 4, 2, 4, 1, 3, 0}; return structs_col{{child0, child1}}; }; return cudf::make_lists_column(11, diff --git a/cpp/tests/table/experimental_row_operator_tests.cu b/cpp/tests/table/experimental_row_operator_tests.cu index 9e305ffeabd..5ae1c7d9729 100644 --- a/cpp/tests/table/experimental_row_operator_tests.cu +++ b/cpp/tests/table/experimental_row_operator_tests.cu @@ -265,10 +265,10 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTables) using data_col = cudf::test::fixed_width_column_wrapper; using int32s_col = cudf::test::fixed_width_column_wrapper; - auto const col1 = data_col{5, 2, 7, 1, 3}; - auto const col2 = data_col{}; // empty - auto const lhs = cudf::table_view{{col1}}; - auto const rhs = cudf::table_view{{col2}}; + auto const col1 = data_col{5, 2, 7, 1, 3}; + auto const col2 = data_col{}; // empty + auto const lhs = cudf::table_view{{col1}}; + auto const empty_rhs = cudf::table_view{{col2}}; auto const stream = cudf::get_default_stream(); auto const test_sort = [stream](auto const& preprocessed, @@ -281,7 +281,7 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTables) }; auto const test_sort_two_tables = [&](auto const& preprocessed_lhs, - auto const& preprocessed_rhs) { + auto const& preprocessed_empty_rhs) { auto const expected_lhs = int32s_col{3, 1, 4, 0, 2}; test_sort(preprocessed_lhs, lhs, @@ -292,31 +292,31 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTables) cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, expected_lhs); - auto const expected_rhs = int32s_col{}; - test_sort(preprocessed_rhs, - rhs, + auto const expected_empty_rhs = int32s_col{}; + test_sort(preprocessed_empty_rhs, + empty_rhs, cudf::experimental::row::lexicographic::physical_element_comparator{}, - expected_rhs); - test_sort(preprocessed_rhs, - rhs, + expected_empty_rhs); + test_sort(preprocessed_empty_rhs, + empty_rhs, cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, - expected_rhs); + expected_empty_rhs); }; // Generate preprocessed data for both lhs and lhs at the same time. // Switching order of lhs and rhs tables then sorting them using their preprocessed data should // produce exactly the same result. { - auto const [preprocessed_lhs, preprocessed_rhs /*empty*/] = + auto const [preprocessed_lhs, preprocessed_empty_rhs] = cudf::experimental::row::lexicographic::preprocessed_table::create( - lhs, rhs /*empty*/, std::vector{cudf::order::ASCENDING}, {}, stream); - test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + lhs, empty_rhs, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_empty_rhs); } { - auto const [preprocessed_rhs /*empty*/, preprocessed_lhs] = + auto const [preprocessed_empty_rhs, preprocessed_lhs] = cudf::experimental::row::lexicographic::preprocessed_table::create( - rhs /*empty*/, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); - test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + empty_rhs, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_empty_rhs); } } @@ -347,7 +347,7 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) auto const column_order = std::vector{cudf::order::ASCENDING}; auto const lhs = cudf::table_view{{*col1}}; - auto const rhs = cudf::table_view{{*col2}}; + auto const empty_rhs = cudf::table_view{{*col2}}; auto const stream = cudf::get_default_stream(); auto const test_sort = [stream](auto const& preprocessed, @@ -360,28 +360,28 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) }; auto const test_sort_two_tables = [&](auto const& preprocessed_lhs, - auto const& preprocessed_rhs) { + auto const& preprocessed_empty_rhs) { auto const expected_lhs = int32s_col{1, 0}; test_sort(preprocessed_lhs, lhs, cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, expected_lhs); - auto const expected_rhs = int32s_col{}; - test_sort(preprocessed_rhs, - rhs, + auto const expected_empty_rhs = int32s_col{}; + test_sort(preprocessed_empty_rhs, + empty_rhs, cudf::experimental::row::lexicographic::sorting_physical_element_comparator{}, - expected_rhs); + expected_empty_rhs); EXPECT_THROW(test_sort(preprocessed_lhs, lhs, cudf::experimental::row::lexicographic::physical_element_comparator{}, expected_lhs), cudf::logic_error); - EXPECT_THROW(test_sort(preprocessed_rhs, - rhs, + EXPECT_THROW(test_sort(preprocessed_empty_rhs, + empty_rhs, cudf::experimental::row::lexicographic::physical_element_comparator{}, - expected_rhs), + expected_empty_rhs), cudf::logic_error); }; @@ -389,16 +389,16 @@ TYPED_TEST(TypedTableViewTest, TestSortSameTableFromTwoTablesWithListsOfStructs) // Switching order of lhs and rhs tables then sorting them using their preprocessed data should // produce exactly the same result. { - auto const [preprocessed_lhs, preprocessed_rhs /*empty*/] = + auto const [preprocessed_lhs, preprocessed_empty_rhs] = cudf::experimental::row::lexicographic::preprocessed_table::create( - lhs, rhs /*empty*/, std::vector{cudf::order::ASCENDING}, {}, stream); - test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + lhs, empty_rhs, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_empty_rhs); } { - auto const [preprocessed_rhs /*empty*/, preprocessed_lhs] = + auto const [preprocessed_empty_rhs, preprocessed_lhs] = cudf::experimental::row::lexicographic::preprocessed_table::create( - rhs /*empty*/, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); - test_sort_two_tables(preprocessed_lhs, preprocessed_rhs /*empty*/); + empty_rhs, lhs, std::vector{cudf::order::ASCENDING}, {}, stream); + test_sort_two_tables(preprocessed_lhs, preprocessed_empty_rhs); } } From ed89fc3432e7fd4cf411b166051a1866d113524e Mon Sep 17 00:00:00 2001 From: Nghia Truong Date: Tue, 2 May 2023 21:31:12 -0700 Subject: [PATCH 140/140] Add a complex unit test --- cpp/tests/search/search_list_test.cpp | 117 ++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/cpp/tests/search/search_list_test.cpp b/cpp/tests/search/search_list_test.cpp index cc42c467e90..48711c21715 100644 --- a/cpp/tests/search/search_list_test.cpp +++ b/cpp/tests/search/search_list_test.cpp @@ -551,3 +551,120 @@ TEST_F(ListBinarySearch, ListsOfEqualStructsInTwoTables) CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, *result_lower_bound, verbosity); CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, *result_upper_bound, verbosity); } + +TEST_F(ListBinarySearch, CrazyListTest) +{ + // Data type: List>>>>> + + // Haystack must be pre-sorted. + auto const haystack = [] { + auto lists_of_structs_of_ints = [] { + auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 16, 18}; + // clang-format off + auto data1 = int32s_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 4, 6 + }; + auto data2 = int32s_col{1, 2, + 3, + 3, + 3, + 4, 5, + 4, 5, 4, + 4, 5, 4, + 4, 5, 4, + 5, 1 + }; + // clang-format on + auto child = structs_col{{data1, data2}}; + return cudf::make_lists_column(9, offsets.release(), child.release(), 0, {}); + }(); + + auto struct_nested0 = [&] { + std::vector> child_columns; + child_columns.emplace_back(std::move(lists_of_structs_of_ints)); + return cudf::make_structs_column(9, std::move(child_columns), 0, {}); + }(); + + auto struct_nested1 = [&] { + std::vector> child_columns; + child_columns.emplace_back(std::move(struct_nested0)); + return cudf::make_structs_column(9, std::move(child_columns), 0, {}); + }(); + + auto list_nested0 = [&] { + auto offsets = int32s_col{0, 3, 3, 4, 6, 9}; + return cudf::make_lists_column(5, offsets.release(), std::move(struct_nested1), 0, {}); + }(); + + auto offsets = int32s_col{0, 0, 2, 4, 5, 5}; + return cudf::make_lists_column(5, offsets.release(), std::move(list_nested0), 0, {}); + }(); + + auto const needles = [] { + auto lists_of_structs_of_ints = [] { + auto offsets = int32s_col{0, 2, 3, 4, 5, 7, 10, 13, 15, 17}; + // clang-format off + auto data1 = int32s_col{1, 2, + 3, + 4, + 5, + 4, 5, + 5, 5, 4, + 4, 5, 4, + 4, 4, + 4, 6 + }; + auto data2 = int32s_col{1, 2, + 3, + 4, + 5, + 4, 5, + 5, 5, 4, + 4, 5, 4, + 4, 4, + 5, 1 + }; + // clang-format on + auto child = structs_col{{data1, data2}}; + return cudf::make_lists_column(9, offsets.release(), child.release(), 0, {}); + }(); + + auto struct_nested0 = [&] { + std::vector> child_columns; + child_columns.emplace_back(std::move(lists_of_structs_of_ints)); + return cudf::make_structs_column(9, std::move(child_columns), 0, {}); + }(); + + auto struct_nested1 = [&] { + std::vector> child_columns; + child_columns.emplace_back(std::move(struct_nested0)); + return cudf::make_structs_column(9, std::move(child_columns), 0, {}); + }(); + + auto list_nested0 = [&] { + auto offsets = int32s_col{0, 3, 3, 4, 6, 9}; + return cudf::make_lists_column(5, offsets.release(), std::move(struct_nested1), 0, {}); + }(); + + auto offsets = int32s_col{0, 2, 2, 4, 4, 5}; + return cudf::make_lists_column(5, offsets.release(), std::move(list_nested0), 0, {}); + }(); + + // In this search, the two table have many equal structs. + // This help to verify the internal implementation of two-table lex comparator in which the + // structs column of two input tables are concatenated, ranked, then split. + auto const [result_lower_bound, result_upper_bound] = search_bounds( + cudf::table_view{{*haystack}}, cudf::table_view{{*needles}}, {cudf::order::ASCENDING}); + + auto const expected_lower_bound = int32s_col{2, 0, 5, 0, 5}; + auto const expected_upper_bound = int32s_col{2, 1, 5, 1, 5}; + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_lower_bound, *result_lower_bound, verbosity); + CUDF_TEST_EXPECT_COLUMNS_EQUAL(expected_upper_bound, *result_upper_bound, verbosity); +}