Skip to content

Commit f938467

Browse files
authored
Fix min/max reduction logic for dictionary columns (rapidsai#20847)
Fixes the logic for the min, max, and minmax aggregration types in `cudf::reduce` for dictionary types. The previous implementation assumes the keys are sorted and therefore only the indices are required. The changes correctly computes the aggregation using the keys/indices appropriately. This is part of a larger effort to no longer require sorted keys in dictionary columns. Authors: - David Wendt (https://github.com/davidwendt) Approvers: - Bradley Dice (https://github.com/bdice) - Yunsong Wang (https://github.com/PointKernel) URL: rapidsai#20847
1 parent b337d14 commit f938467

5 files changed

Lines changed: 33 additions & 29 deletions

File tree

cpp/include/cudf/reduction/detail/reduction_functions.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ std::unique_ptr<scalar> argmax(column_view const& col,
129129
rmm::cuda_stream_view stream,
130130
rmm::device_async_resource_ref mr);
131131

132+
/**
133+
* @brief Computes the minimum and maximum values of the input column
134+
*
135+
* @param col input column to compute minmax
136+
* @param stream CUDA stream used for device memory operations and kernel launches
137+
* @param mr Device memory resource used to allocate the returned column's device memory
138+
* @return A pair consisting of the minimum value and the maximum value
139+
*/
140+
std::pair<std::unique_ptr<scalar>, std::unique_ptr<scalar>> minmax(
141+
cudf::column_view const& col, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr);
142+
132143
/**
133144
* @brief Computes any of elements in input column is true when typecasted to bool
134145
*

cpp/src/reductions/max.cu

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: Copyright (c) 2019-2024, NVIDIA CORPORATION.
2+
* SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

@@ -24,12 +24,9 @@ std::unique_ptr<cudf::scalar> max(column_view const& col,
2424
auto const input_type =
2525
cudf::is_dictionary(col.type()) ? cudf::dictionary_column_view(col).keys().type() : col.type();
2626
CUDF_EXPECTS(input_type == output_dtype, "max() operation requires matching output type");
27-
auto const dispatch_type = cudf::is_dictionary(col.type())
28-
? cudf::dictionary_column_view(col).indices().type()
29-
: col.type();
3027

3128
using reducer = simple::detail::same_element_type_dispatcher<op::max>;
32-
return cudf::type_dispatcher(dispatch_type, reducer{}, col, init, stream, mr);
29+
return cudf::type_dispatcher(input_type, reducer{}, col, init, stream, mr);
3330
}
3431

3532
} // namespace detail

cpp/src/reductions/min.cu

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: Copyright (c) 2019-2024, NVIDIA CORPORATION.
2+
* SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

@@ -21,12 +21,9 @@ std::unique_ptr<cudf::scalar> min(column_view const& col,
2121
auto const input_type =
2222
cudf::is_dictionary(col.type()) ? cudf::dictionary_column_view(col).keys().type() : col.type();
2323
CUDF_EXPECTS(input_type == output_dtype, "min() operation requires matching output type");
24-
auto const dispatch_type = cudf::is_dictionary(col.type())
25-
? cudf::dictionary_column_view(col).indices().type()
26-
: col.type();
2724

2825
using reducer = simple::detail::same_element_type_dispatcher<op::min>;
29-
return cudf::type_dispatcher(dispatch_type, reducer{}, col, init, stream, mr);
26+
return cudf::type_dispatcher(input_type, reducer{}, col, init, stream, mr);
3027
}
3128
} // namespace detail
3229
} // namespace reduction

cpp/src/reductions/minmax.cu

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <cudf/detail/utilities/device_operators.cuh>
1414
#include <cudf/dictionary/dictionary_column_view.hpp>
1515
#include <cudf/reduction.hpp>
16+
#include <cudf/reduction/detail/reduction_functions.hpp>
1617
#include <cudf/scalar/scalar_factories.hpp>
1718
#include <cudf/utilities/default_stream.hpp>
1819
#include <cudf/utilities/memory_resource.hpp>
@@ -30,6 +31,7 @@
3031
#include <type_traits>
3132

3233
namespace cudf {
34+
namespace reduction {
3335
namespace detail {
3436

3537
namespace {
@@ -191,7 +193,7 @@ struct minmax_functor {
191193
auto minimum = new ScalarType(T{}, true, stream, mr);
192194
auto maximum = new ScalarType(T{}, true, stream, mr);
193195
// copy dev_result to the output scalars
194-
device_single_thread(
196+
cudf::detail::device_single_thread(
195197
assign_min_max<storage_type>{dev_result.data(), minimum->data(), maximum->data()}, stream);
196198
return {std::unique_ptr<scalar>(minimum), std::unique_ptr<scalar>(maximum)};
197199
}
@@ -207,8 +209,6 @@ struct minmax_functor {
207209
// compute minimum and maximum values
208210
auto dev_result = reduce<cudf::string_view>(col, stream);
209211
// copy the minmax_pair to the host; does not copy the strings
210-
using OutputType = minmax_pair<cudf::string_view>;
211-
212212
auto const host_result = dev_result.value(stream);
213213
// strings are copied to create the scalars here
214214
return {std::make_unique<string_scalar>(host_result.min_val, true, stream, mr),
@@ -223,15 +223,15 @@ struct minmax_functor {
223223
cudf::column_view const& col, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr)
224224
requires(cudf::is_dictionary<T>())
225225
{
226-
// compute minimum and maximum values
227-
auto dev_result = reduce<T>(col, stream);
228-
// copy the minmax_pair to the host to call get_element
229-
using OutputType = minmax_pair<T>;
230-
OutputType host_result = dev_result.value(stream);
231-
// get the keys for those indexes
232-
auto const keys = dictionary_column_view(col).keys();
233-
return {detail::get_element(keys, static_cast<size_type>(host_result.min_val), stream, mr),
234-
detail::get_element(keys, static_cast<size_type>(host_result.max_val), stream, mr)};
226+
// computes minimum and maximum on the dictionary indices as dictionary32 values
227+
auto d_indices = reduce<T>(col, stream);
228+
auto const indices = d_indices.value(stream);
229+
// use these values to slice the keys column (add 1 for complete inclusion)
230+
auto keys = cudf::detail::slice(dictionary_column_view(col).keys(),
231+
{indices.min_val.value(), indices.max_val.value() + 1},
232+
stream)
233+
.front();
234+
return type_dispatcher(keys.type(), minmax_functor{}, keys, stream, mr);
235235
}
236236

237237
template <typename T>
@@ -263,12 +263,13 @@ std::pair<std::unique_ptr<scalar>, std::unique_ptr<scalar>> minmax(
263263
return type_dispatcher(col.type(), minmax_functor{}, col, stream, mr);
264264
}
265265
} // namespace detail
266+
} // namespace reduction
266267

267268
std::pair<std::unique_ptr<scalar>, std::unique_ptr<scalar>> minmax(
268269
column_view const& col, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr)
269270
{
270271
CUDF_FUNC_RANGE();
271-
return detail::minmax(col, stream, mr);
272+
return reduction::detail::minmax(col, stream, mr);
272273
}
273274

274275
} // namespace cudf

cpp/src/reductions/simple.cuh

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <cudf/dictionary/detail/iterator.cuh>
1414
#include <cudf/dictionary/dictionary_column_view.hpp>
1515
#include <cudf/reduction/detail/reduction.cuh>
16+
#include <cudf/reduction/detail/reduction_functions.hpp>
1617
#include <cudf/scalar/scalar_device_view.cuh>
1718
#include <cudf/scalar/scalar_factories.hpp>
1819
#include <cudf/utilities/memory_resource.hpp>
@@ -332,12 +333,9 @@ struct same_element_type_dispatcher {
332333
if (!cudf::is_dictionary(col.type())) {
333334
return simple_reduction<ElementType, ElementType, Op>(col, init, stream, mr);
334335
}
335-
auto index = simple_reduction<ElementType, ElementType, Op>(
336-
dictionary_column_view(col).get_indices_annotated(),
337-
init,
338-
stream,
339-
cudf::get_current_device_resource_ref());
340-
return resolve_key<ElementType>(dictionary_column_view(col).keys(), *index, stream, mr);
336+
auto mm = cudf::reduction::detail::minmax(col, stream, mr);
337+
return std::is_same_v<Op, cudf::reduction::detail::op::min> ? std::move(mm.first)
338+
: std::move(mm.second);
341339
}
342340

343341
template <typename ElementType>

0 commit comments

Comments
 (0)