Skip to content

Commit

Permalink
Convert rank to use to experimental row comparators (#12481)
Browse files Browse the repository at this point in the history
Converts the `rank` function to use experimental row comparators, which support list and struct types. Part of #11844.

[Throughput benchmarks](#12481 (comment)) are available below. It seems like when `size_bytes` is constrained, the generator generates fewer rows in `list` types for increasing depths. That's why, `depth=4` has a higher throughput than `depth=1` because the number of leaf elements generated are the same, but with much fewer rows.

Authors:
  - Divye Gala (https://github.com/divyegala)
  - Jordan Jacobelli (https://github.com/Ethyling)

Approvers:
  - Bradley Dice (https://github.com/bdice)
  - Yunsong Wang (https://github.com/PointKernel)
  - AJ Schmidt (https://github.com/ajschmidt8)

URL: #12481
  • Loading branch information
divyegala committed Feb 8, 2023
1 parent 37fe468 commit b87b64f
Show file tree
Hide file tree
Showing 10 changed files with 776 additions and 129 deletions.
5 changes: 4 additions & 1 deletion cpp/benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ 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/segmented_sort.cpp sort/sort_lists.cpp sort/sort_structs.cpp)
ConfigureNVBench(
SORT_NVBENCH sort/rank_lists.cpp sort/rank_structs.cpp sort/segmented_sort.cpp
sort/sort_lists.cpp sort/sort_structs.cpp
)

# ##################################################################################################
# * quantiles benchmark
Expand Down
85 changes: 85 additions & 0 deletions cpp/benchmarks/sort/nested_types_common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.
*/

#pragma once

#include <benchmarks/common/generate_input.hpp>
#include <benchmarks/fixture/rmm_pool_raii.hpp>

#include <cudf_test/column_wrapper.hpp>

#include <nvbench/nvbench.cuh>

#include <random>

inline std::unique_ptr<cudf::table> create_lists_data(nvbench::state& state)
{
const size_t size_bytes(state.get_int64("size_bytes"));
const cudf::size_type depth{static_cast<cudf::size_type>(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_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);
}

inline std::unique_ptr<cudf::table> create_structs_data(nvbench::state& state,
cudf::size_type const n_cols = 1)
{
using Type = int;
using column_wrapper = cudf::test::fixed_width_column_wrapper<Type>;
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(0, 100);

const cudf::size_type n_rows{static_cast<cudf::size_type>(state.get_int64("NumRows"))};
const cudf::size_type depth{static_cast<cudf::size_type>(state.get_int64("Depth"))};
const bool nulls{static_cast<bool>(state.get_int64("Nulls"))};

// Create columns with values in the range [0,100)
std::vector<column_wrapper> columns;
columns.reserve(n_cols);
std::generate_n(std::back_inserter(columns), n_cols, [&]() {
auto const elements = cudf::detail::make_counting_transform_iterator(
0, [&](auto row) { return distribution(generator); });
if (!nulls) return column_wrapper(elements, elements + n_rows);
auto valids =
cudf::detail::make_counting_transform_iterator(0, [](auto i) { return i % 10 != 0; });
return column_wrapper(elements, elements + n_rows, valids);
});

std::vector<std::unique_ptr<cudf::column>> cols;
std::transform(columns.begin(), columns.end(), std::back_inserter(cols), [](column_wrapper& col) {
return col.release();
});

std::vector<std::unique_ptr<cudf::column>> child_cols = std::move(cols);
// Nest the child columns in a struct, then nest that struct column inside another
// struct column up to the desired depth
for (int i = 0; i < depth; i++) {
std::vector<bool> struct_validity;
std::uniform_int_distribution<int> bool_distribution(0, 100 * (i + 1));
std::generate_n(
std::back_inserter(struct_validity), n_rows, [&]() { return bool_distribution(generator); });
cudf::test::structs_column_wrapper struct_col(std::move(child_cols), struct_validity);
child_cols = std::vector<std::unique_ptr<cudf::column>>{};
child_cols.push_back(struct_col.release());
}

// Create table view
return std::make_unique<cudf::table>(std::move(child_cols));
}
4 changes: 2 additions & 2 deletions cpp/benchmarks/sort/rank.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -33,7 +33,7 @@ static void BM_rank(benchmark::State& state, bool nulls)
// Create columns with values in the range [0,100)
data_profile profile = data_profile_builder().cardinality(0).distribution(
cudf::type_to_id<Type>(), distribution_id::UNIFORM, 0, 100);
profile.set_null_probability(nulls ? std::optional{0.01} : std::nullopt);
profile.set_null_probability(nulls ? std::optional{0.2} : std::nullopt);
auto keys = create_random_column(cudf::type_to_id<Type>(), row_count{n_rows}, profile);

for (auto _ : state) {
Expand Down
49 changes: 49 additions & 0 deletions cpp/benchmarks/sort/rank_lists.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 "nested_types_common.hpp"
#include "rank_types_common.hpp"

#include <cudf/sorting.hpp>

#include <cudf_test/column_utilities.hpp>

#include <nvbench/nvbench.cuh>

template <cudf::rank_method method>
void nvbench_rank_lists(nvbench::state& state, nvbench::type_list<nvbench::enum_type<method>>)
{
cudf::rmm_pool_raii pool_raii;

auto const table = create_lists_data(state);

auto const null_frequency{state.get_float64("null_frequency")};

state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) {
cudf::rank(table->view().column(0),
method,
cudf::order::ASCENDING,
null_frequency ? cudf::null_policy::INCLUDE : cudf::null_policy::EXCLUDE,
cudf::null_order::AFTER,
rmm::mr::get_current_device_resource());
});
}

NVBENCH_BENCH_TYPES(nvbench_rank_lists, NVBENCH_TYPE_AXES(methods))
.set_name("rank_lists")
.add_int64_power_of_two_axis("size_bytes", {10, 18, 24, 28})
.add_int64_axis("depth", {1, 4})
.add_float64_axis("null_frequency", {0, 0.2});
47 changes: 47 additions & 0 deletions cpp/benchmarks/sort/rank_structs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 "nested_types_common.hpp"
#include "rank_types_common.hpp"

#include <cudf/sorting.hpp>

#include <nvbench/nvbench.cuh>

template <cudf::rank_method method>
void nvbench_rank_structs(nvbench::state& state, nvbench::type_list<nvbench::enum_type<method>>)
{
cudf::rmm_pool_raii pool_raii;

auto const table = create_structs_data(state);

const bool nulls{static_cast<bool>(state.get_int64("Nulls"))};

state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) {
cudf::rank(table->view().column(0),
method,
cudf::order::ASCENDING,
nulls ? cudf::null_policy::INCLUDE : cudf::null_policy::EXCLUDE,
cudf::null_order::AFTER,
rmm::mr::get_current_device_resource());
});
}

NVBENCH_BENCH_TYPES(nvbench_rank_structs, NVBENCH_TYPE_AXES(methods))
.set_name("rank_structs")
.add_int64_power_of_two_axis("NumRows", {10, 18, 26})
.add_int64_axis("Depth", {0, 1, 8})
.add_int64_axis("Nulls", {0, 1});
52 changes: 52 additions & 0 deletions cpp/benchmarks/sort/rank_types_common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.
*/

#pragma once

#include <cudf/aggregation.hpp>

#include <nvbench/nvbench.cuh>

enum class rank_method : int32_t {};

NVBENCH_DECLARE_ENUM_TYPE_STRINGS(
cudf::rank_method,
[](cudf::rank_method value) {
switch (value) {
case cudf::rank_method::FIRST: return "FIRST";
case cudf::rank_method::AVERAGE: return "AVERAGE";
case cudf::rank_method::MIN: return "MIN";
case cudf::rank_method::MAX: return "MAX";
case cudf::rank_method::DENSE: return "DENSE";
default: return "unknown";
}
},
[](cudf::rank_method value) {
switch (value) {
case cudf::rank_method::FIRST: return "cudf::rank_method::FIRST";
case cudf::rank_method::AVERAGE: return "cudf::rank_method::AVERAGE";
case cudf::rank_method::MIN: return "cudf::rank_method::MIN";
case cudf::rank_method::MAX: return "cudf::rank_method::MAX";
case cudf::rank_method::DENSE: return "cudf::rank_method::DENSE";
default: return "unknown";
}
})

using methods = nvbench::enum_type_list<cudf::rank_method::AVERAGE,
cudf::rank_method::DENSE,
cudf::rank_method::FIRST,
cudf::rank_method::MAX,
cudf::rank_method::MIN>;
16 changes: 3 additions & 13 deletions cpp/benchmarks/sort/sort_lists.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -14,8 +14,7 @@
* limitations under the License.
*/

#include <benchmarks/common/generate_input.hpp>
#include <benchmarks/fixture/rmm_pool_raii.hpp>
#include "nested_types_common.hpp"

#include <cudf/detail/sorting.hpp>

Expand All @@ -25,16 +24,7 @@ void nvbench_sort_lists(nvbench::state& state)
{
cudf::rmm_pool_raii pool_raii;

const size_t size_bytes(state.get_int64("size_bytes"));
const cudf::size_type depth{static_cast<cudf::size_type>(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_list_depth(depth);
table_profile.set_null_probability(null_frequency);
auto const table =
create_random_table({cudf::type_id::LIST}, table_size_bytes{size_bytes}, table_profile);
auto const table = create_lists_data(state);

state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) {
rmm::cuda_stream_view stream_view{launch.get_stream()};
Expand Down
52 changes: 4 additions & 48 deletions cpp/benchmarks/sort/sort_structs.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -14,65 +14,21 @@
* limitations under the License.
*/

#include <benchmarks/fixture/rmm_pool_raii.hpp>

#include <cudf_test/column_wrapper.hpp>
#include "nested_types_common.hpp"

#include <cudf/detail/sorting.hpp>

#include <nvbench/nvbench.cuh>

#include <random>

void nvbench_sort_struct(nvbench::state& state)
{
cudf::rmm_pool_raii pool_raii;

using Type = int;
using column_wrapper = cudf::test::fixed_width_column_wrapper<Type>;
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(0, 100);

const cudf::size_type n_rows{static_cast<cudf::size_type>(state.get_int64("NumRows"))};
const cudf::size_type n_cols{1};
const cudf::size_type depth{static_cast<cudf::size_type>(state.get_int64("Depth"))};
const bool nulls{static_cast<bool>(state.get_int64("Nulls"))};

// Create columns with values in the range [0,100)
std::vector<column_wrapper> columns;
columns.reserve(n_cols);
std::generate_n(std::back_inserter(columns), n_cols, [&]() {
auto const elements = cudf::detail::make_counting_transform_iterator(
0, [&](auto row) { return distribution(generator); });
if (!nulls) return column_wrapper(elements, elements + n_rows);
auto valids =
cudf::detail::make_counting_transform_iterator(0, [](auto i) { return i % 10 != 0; });
return column_wrapper(elements, elements + n_rows, valids);
});

std::vector<std::unique_ptr<cudf::column>> cols;
std::transform(columns.begin(), columns.end(), std::back_inserter(cols), [](column_wrapper& col) {
return col.release();
});

std::vector<std::unique_ptr<cudf::column>> child_cols = std::move(cols);
// Lets add some layers
for (int i = 0; i < depth; i++) {
std::vector<bool> struct_validity;
std::uniform_int_distribution<int> bool_distribution(0, 100 * (i + 1));
std::generate_n(
std::back_inserter(struct_validity), n_rows, [&]() { return bool_distribution(generator); });
cudf::test::structs_column_wrapper struct_col(std::move(child_cols), struct_validity);
child_cols = std::vector<std::unique_ptr<cudf::column>>{};
child_cols.push_back(struct_col.release());
}

// Create table view
auto const input = cudf::table(std::move(child_cols));
auto const input = create_structs_data(state);

state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) {
rmm::cuda_stream_view stream_view{launch.get_stream()};
cudf::detail::sorted_order(input, {}, {}, stream_view, rmm::mr::get_current_device_resource());
cudf::detail::sorted_order(*input, {}, {}, stream_view, rmm::mr::get_current_device_resource());
});
}

Expand Down

0 comments on commit b87b64f

Please sign in to comment.