From b3043bc70cc033706491df678c260fe3eecd56d3 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Wed, 25 Feb 2026 15:39:24 -0800 Subject: [PATCH 01/64] feat(cudf): add decimal sum/avg kernels and aggregation support Add CUDA kernels for decimal intermediate-state serialization/deserialization and average computation, wire them into cuDF hash aggregation, and introduce a focused decimal aggregation test suite. --- velox/experimental/cudf/exec/CMakeLists.txt | 3 + .../cudf/exec/CudfHashAggregation.cpp | 515 +++++- .../cudf/exec/CudfHashAggregation.h | 3 +- .../cudf/exec/DecimalAggregationKernels.cu | 432 +++++ .../cudf/exec/DecimalAggregationKernels.h | 56 + .../cudf/exec/VeloxCudfInterop.cpp | 6 - .../experimental/cudf/exec/VeloxCudfInterop.h | 2 - velox/experimental/cudf/tests/CMakeLists.txt | 19 + .../cudf/tests/DecimalAggregationTest.cpp | 1407 +++++++++++++++++ 9 files changed, 2389 insertions(+), 54 deletions(-) create mode 100644 velox/experimental/cudf/exec/DecimalAggregationKernels.cu create mode 100644 velox/experimental/cudf/exec/DecimalAggregationKernels.h create mode 100644 velox/experimental/cudf/tests/DecimalAggregationTest.cpp diff --git a/velox/experimental/cudf/exec/CMakeLists.txt b/velox/experimental/cudf/exec/CMakeLists.txt index 45048585e1c..67d78e0d3ec 100644 --- a/velox/experimental/cudf/exec/CMakeLists.txt +++ b/velox/experimental/cudf/exec/CMakeLists.txt @@ -21,6 +21,7 @@ add_library( CudfEnforceSingleRow.cpp CudfFilterProject.cpp CudfHashAggregation.cpp + DecimalAggregationKernels.cu CudfHashJoin.cpp CudfLimit.cpp CudfLocalPartition.cpp @@ -37,6 +38,8 @@ add_library( VeloxCudfInterop.cpp ) +set_target_properties(velox_cudf_exec PROPERTIES CUDA_STANDARD 20 CUDA_STANDARD_REQUIRED ON) + target_link_libraries( velox_cudf_exec PUBLIC cudf::cudf diff --git a/velox/experimental/cudf/exec/CudfHashAggregation.cpp b/velox/experimental/cudf/exec/CudfHashAggregation.cpp index f6c07fbed6f..8fcc59c532e 100644 --- a/velox/experimental/cudf/exec/CudfHashAggregation.cpp +++ b/velox/experimental/cudf/exec/CudfHashAggregation.cpp @@ -19,6 +19,7 @@ #include "velox/experimental/cudf/exec/AggregationRegistry.h" #include "velox/experimental/cudf/exec/CudfFilterProject.h" #include "velox/experimental/cudf/exec/CudfHashAggregation.h" +#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/Utilities.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" @@ -41,6 +42,7 @@ #include #include #include +#include #include #include @@ -70,7 +72,8 @@ using facebook::velox::cudf_velox::get_temp_mr; \ void addGroupbyRequest( \ cudf::table_view const& tbl, \ - std::vector& requests) override { \ + std::vector& requests, \ + rmm::cuda_stream_view stream) override { \ VELOX_CHECK( \ constant == nullptr, \ #Name "Aggregator does not yet support constant input"); \ @@ -85,10 +88,9 @@ using facebook::velox::cudf_velox::get_temp_mr; std::vector& results, \ rmm::cuda_stream_view stream) override { \ auto col = std::move(results[output_idx].results[0]); \ - const auto cudfType = \ - cudf::data_type(cudf_velox::veloxToCudfTypeId(resultType)); \ - if (col->type() != cudfType) { \ - col = cudf::cast(*col, cudfType, stream, get_output_mr()); \ + auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); \ + if (col->type() != cudfResType) { \ + col = cudf::cast(*col, cudfResType, stream, get_output_mr()); \ } \ return col; \ } \ @@ -100,12 +102,11 @@ using facebook::velox::cudf_velox::get_temp_mr; vector_size_t /*inputRowCount*/) override { \ auto const aggRequest = \ cudf::make_##name##_aggregation(); \ - auto const cudfOutputType = \ - cudf::data_type(cudf_velox::veloxToCudfTypeId(outputType)); \ + auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); \ auto const resultScalar = cudf::reduce( \ input.column(inputIndex), \ *aggRequest, \ - cudfOutputType, \ + cudfOutType, \ stream, \ get_temp_mr()); \ return cudf::make_column_from_scalar( \ @@ -120,6 +121,308 @@ DEFINE_SIMPLE_AGGREGATOR(Sum, sum, SUM) DEFINE_SIMPLE_AGGREGATOR(Min, min, MIN) DEFINE_SIMPLE_AGGREGATOR(Max, max, MAX) +struct DecimalSumOrAvgAggregator : cudf_velox::CudfHashAggregation::Aggregator { + DecimalSumOrAvgAggregator( + core::AggregationNode::Step step, + uint32_t inputIndex, + VectorPtr constant, + bool isGlobal, + const TypePtr& resultType, + const bool isAvg) + : Aggregator( + step, + cudf::aggregation::SUM, + inputIndex, + constant, + isGlobal, + resultType), + isAvg_(isAvg) {} + + void addGroupbyRequest( + cudf::table_view const& tbl, + std::vector& requests, + rmm::cuda_stream_view stream) override { + if (step == core::AggregationNode::Step::kIntermediate && + tbl.column(inputIndex).type().id() == cudf::type_id::STRING) { + auto scale = resultType->isDecimal() + ? getDecimalPrecisionScale(*resultType).second + : 0; + auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); + decodedSum_ = std::move(decoded.sum); + decodedCount_ = std::move(decoded.count); + + sumIdx_ = requests.size(); + auto& sumRequest = requests.emplace_back(); + sumRequest.values = decodedSum_->view(); + sumRequest.aggregations.push_back( + cudf::make_sum_aggregation()); + + countIdx_ = requests.size(); + auto& countRequest = requests.emplace_back(); + countRequest.values = decodedCount_->view(); + countRequest.aggregations.push_back( + cudf::make_sum_aggregation()); + return; + } + + if (step == core::AggregationNode::Step::kFinal && + tbl.column(inputIndex).type().id() == cudf::type_id::STRING) { + auto scale = getDecimalPrecisionScale(*resultType).second; + if (isAvg_) { + auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); + decodedSum_ = std::move(decoded.sum); + decodedCount_ = std::move(decoded.count); + + sumIdx_ = requests.size(); + auto& sumRequest = requests.emplace_back(); + sumRequest.values = decodedSum_->view(); + sumRequest.aggregations.push_back( + cudf::make_sum_aggregation()); + + countIdx_ = requests.size(); + auto& countRequest = requests.emplace_back(); + countRequest.values = decodedCount_->view(); + countRequest.aggregations.push_back( + cudf::make_sum_aggregation()); + return; + } else { + auto& request = requests.emplace_back(); + sumIdx_ = requests.size() - 1; + decodedSum_ = cudf_velox::deserializeDecimalSumState( + tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); + request.values = decodedSum_->view(); + request.aggregations.push_back( + cudf::make_sum_aggregation()); + return; + } + } else { + auto& request = requests.emplace_back(); + sumIdx_ = requests.size() - 1; + request.values = tbl.column(inputIndex); + request.aggregations.push_back( + cudf::make_sum_aggregation()); + if (step == core::AggregationNode::Step::kPartial || + (step == core::AggregationNode::Step::kSingle && isAvg_)) { + request.aggregations.push_back( + cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE)); + } + return; + } + } + + std::unique_ptr makeOutputColumn( + std::vector& results, + rmm::cuda_stream_view stream) override { + auto col = std::move(results[sumIdx_].results[0]); + if (isAvg_ && step == core::AggregationNode::Step::kSingle) { + auto count = std::move(results[sumIdx_].results[1]); + return computeAvgColumn(std::move(col), std::move(count), stream); + } + if (step == core::AggregationNode::Step::kPartial) { + auto count = std::move(results[sumIdx_].results[1]); + if (count->type().id() != cudf::type_id::INT64) { + count = cudf::cast( + *count, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_output_mr()); + } + return cudf_velox::serializeDecimalSumState( + col->view(), count->view(), stream, cudf_velox::get_output_mr()); + } + if (step == core::AggregationNode::Step::kIntermediate) { + auto count = std::move(results[countIdx_].results[0]); + if (count->type().id() != cudf::type_id::INT64) { + count = cudf::cast( + *count, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_output_mr()); + } + return cudf_velox::serializeDecimalSumState( + col->view(), count->view(), stream, cudf_velox::get_output_mr()); + } + if (isAvg_ && step == core::AggregationNode::Step::kFinal) { + auto count = std::move(results[countIdx_].results[0]); + return computeAvgColumn(std::move(col), std::move(count), stream); + } + auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); + if (col->type() != cudfResType) { + col = cudf::cast(*col, cudfResType, stream, cudf_velox::get_output_mr()); + } + return col; + } + + std::unique_ptr doReduce( + cudf::table_view const& input, + TypePtr const& outputType, + rmm::cuda_stream_view stream) override { + if (step == core::AggregationNode::Step::kSingle && isAvg_) { + auto const sumAgg = + cudf::make_sum_aggregation(); + cudf::column_view inputCol = input.column(inputIndex); + auto sumScalar = cudf::reduce( + inputCol, + *sumAgg, + inputCol.type(), + stream, + cudf_velox::get_temp_mr()); + auto countAgg = cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE); + auto countScalar = cudf::reduce( + inputCol, + *countAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_temp_mr()); + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, cudf_velox::get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, cudf_velox::get_output_mr()); + return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); + } + auto const aggRequest = + cudf::make_sum_aggregation(); + cudf::column_view inputCol = input.column(inputIndex); + if (step == core::AggregationNode::Step::kPartial) { + auto sumScalar = cudf::reduce( + inputCol, + *aggRequest, + inputCol.type(), + stream, + cudf_velox::get_temp_mr()); + auto countAgg = cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE); + auto countScalar = cudf::reduce( + inputCol, + *countAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_temp_mr()); + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, cudf_velox::get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, cudf_velox::get_output_mr()); + return cudf_velox::serializeDecimalSumState( + sumCol->view(), + countCol->view(), + stream, + cudf_velox::get_output_mr()); + } + if (step == core::AggregationNode::Step::kIntermediate && + inputCol.type().id() == cudf::type_id::STRING) { + auto scale = outputType->isDecimal() + ? getDecimalPrecisionScale(*outputType).second + : 0; + auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + inputCol, scale, stream, cudf_velox::get_output_mr()); + auto sumScalar = cudf::reduce( + decoded.sum->view(), + *aggRequest, + decoded.sum->view().type(), + stream, + cudf_velox::get_temp_mr()); + auto countScalar = cudf::reduce( + decoded.count->view(), + *aggRequest, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_temp_mr()); + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, cudf_velox::get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, cudf_velox::get_output_mr()); + return cudf_velox::serializeDecimalSumState( + sumCol->view(), + countCol->view(), + stream, + cudf_velox::get_output_mr()); + } + if (step == core::AggregationNode::Step::kFinal && + inputCol.type().id() == cudf::type_id::STRING) { + auto scale = getDecimalPrecisionScale(*outputType).second; + if (isAvg_) { + // AVG + // deserialize the results (sum and count) + auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( + inputCol, scale, stream, cudf_velox::get_output_mr()); + // reduce the two results to get final sum and count scalars + auto sumScalar = cudf::reduce( + sumAndCount.sum->view(), + *aggRequest, + sumAndCount.sum->view().type(), + stream, + cudf_velox::get_temp_mr()); + auto countScalar = cudf::reduce( + sumAndCount.count->view(), + *aggRequest, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_temp_mr()); + // convert to columns in order to perform division, as we cannot divide + // scalars directly + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, cudf_velox::get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, cudf_velox::get_output_mr()); + return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); + } else { + // SUM + decodedSum_ = cudf_velox::deserializeDecimalSumState( + inputCol, scale, stream, cudf_velox::get_output_mr()); + inputCol = decodedSum_->view(); + // @TODO does this need to drop through to the code below + // or can we just do that stuff here, and not need decodedSum_ or + // decodedCount_ why we do have those anyway if they're only set in + // addGroupbyRequest() and either overwritten or not even used here? + // what does the final cudf::reduce() below actually do? + } + } + auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); + std::unique_ptr castedInput; + if (outputType->isDecimal() && inputCol.type() != cudfOutType) { + castedInput = cudf::cast( + inputCol, cudfOutType, stream, cudf_velox::get_output_mr()); + inputCol = castedInput->view(); + } + auto const resultScalar = cudf::reduce( + inputCol, *aggRequest, cudfOutType, stream, cudf_velox::get_temp_mr()); + return cudf::make_column_from_scalar( + *resultScalar, 1, stream, cudf_velox::get_output_mr()); + } + + private: + std::unique_ptr computeAvgColumn( + std::unique_ptr sum, + std::unique_ptr count, + rmm::cuda_stream_view stream) const { + if (count->type().id() != cudf::type_id::INT64) { + count = cudf::cast( + *count, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_output_mr()); + } + auto avgCol = cudf_velox::computeDecimalAverage( + sum->view(), count->view(), stream, cudf_velox::get_output_mr()); + auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); + if (avgCol->type() != cudfOutType) { + avgCol = cudf::cast( + avgCol->view(), cudfOutType, stream, cudf_velox::get_output_mr()); + } + return avgCol; + } + + uint32_t sumIdx_{0}; + uint32_t countIdx_{0}; + const bool isAvg_{false}; + std::unique_ptr decodedSum_; + std::unique_ptr decodedCount_; +}; + struct CountAggregator : cudf_velox::CudfHashAggregation::Aggregator { enum class CountInputKind { kColumn, @@ -164,7 +467,8 @@ struct CountAggregator : cudf_velox::CudfHashAggregation::Aggregator { void addGroupbyRequest( cudf::table_view const& tbl, - std::vector& requests) override { + std::vector& requests, + rmm::cuda_stream_view stream) override { auto& request = requests.emplace_back(); outputIndex_ = requests.size() - 1; @@ -245,8 +549,7 @@ struct CountAggregator : cudf_velox::CudfHashAggregation::Aggregator { zero, col->size(), stream, get_output_mr()); } // cudf produces int32 for count but velox expects int64. - const auto cudfOutputType = - cudf::data_type(cudf_velox::veloxToCudfTypeId(resultType)); + const auto cudfOutputType = cudf_velox::veloxToCudfDataType(resultType); if (col->type() != cudfOutputType) { col = cudf::cast(*col, cudfOutputType, stream, get_output_mr()); } @@ -275,7 +578,8 @@ struct MeanAggregator : cudf_velox::CudfHashAggregation::Aggregator { void addGroupbyRequest( cudf::table_view const& tbl, - std::vector& requests) override { + std::vector& requests, + rmm::cuda_stream_view stream) override { switch (step) { case core::AggregationNode::Step::kSingle: { auto& request = requests.emplace_back(); @@ -333,13 +637,12 @@ struct MeanAggregator : cudf_velox::CudfHashAggregation::Aggregator { auto count = std::move(results[sumIdx_].results[1]); auto const size = sum->size(); - auto const cudfSumType = cudf::data_type( - cudf_velox::veloxToCudfTypeId(outputType->childAt(0))); - auto const cudfCountType = cudf::data_type( - cudf_velox::veloxToCudfTypeId(outputType->childAt(1))); - if (sum->type() != cudf::data_type(cudfSumType)) { - sum = cudf::cast( - *sum, cudf::data_type(cudfSumType), stream, get_output_mr()); + auto const cudfSumType = + cudf_velox::veloxToCudfDataType(outputType->childAt(0)); + auto const cudfCountType = + cudf_velox::veloxToCudfDataType(outputType->childAt(1)); + if (sum->type() != cudfSumType) { + sum = cudf::cast(*sum, cudfSumType, stream, get_output_mr()); } if (count->type() != cudf::data_type(cudfCountType)) { count = cudf::cast( @@ -371,13 +674,12 @@ struct MeanAggregator : cudf_velox::CudfHashAggregation::Aggregator { auto count = std::move(results[countIdx_].results[0]); auto size = sum->size(); - auto const cudfSumType = cudf::data_type( - cudf_velox::veloxToCudfTypeId(outputType->childAt(0))); - auto const cudfCountType = cudf::data_type( - cudf_velox::veloxToCudfTypeId(outputType->childAt(1))); - if (sum->type() != cudf::data_type(cudfSumType)) { - sum = cudf::cast( - *sum, cudf::data_type(cudfSumType), stream, get_output_mr()); + auto const cudfSumType = + cudf_velox::veloxToCudfDataType(outputType->childAt(0)); + auto const cudfCountType = + cudf_velox::veloxToCudfDataType(outputType->childAt(1)); + if (sum->type() != cudfSumType) { + sum = cudf::cast(*sum, cudfSumType, stream, get_output_mr()); } if (count->type() != cudf::data_type(cudfCountType)) { count = cudf::cast( @@ -403,7 +705,7 @@ struct MeanAggregator : cudf_velox::CudfHashAggregation::Aggregator { *sum, *count, cudf::binary_operator::DIV, - cudf::data_type(cudf_velox::veloxToCudfTypeId(resultType)), + cudf_velox::veloxToCudfDataType(resultType), stream, get_output_mr()); return avg; @@ -422,8 +724,7 @@ struct MeanAggregator : cudf_velox::CudfHashAggregation::Aggregator { case core::AggregationNode::Step::kSingle: { auto const aggRequest = cudf::make_mean_aggregation(); - auto const cudfOutputType = - cudf::data_type(cudf_velox::veloxToCudfTypeId(outputType)); + auto const cudfOutputType = cudf_velox::veloxToCudfDataType(outputType); auto const resultScalar = cudf::reduce( input.column(inputIndex), *aggRequest, @@ -436,12 +737,8 @@ struct MeanAggregator : cudf_velox::CudfHashAggregation::Aggregator { case core::AggregationNode::Step::kPartial: { VELOX_CHECK(outputType->isRow()); auto const& rowType = outputType->asRow(); - auto const sumType = rowType.childAt(0); - auto const countType = rowType.childAt(1); auto const cudfSumType = - cudf::data_type(cudf_velox::veloxToCudfTypeId(sumType)); - auto const cudfCountType = - cudf::data_type(cudf_velox::veloxToCudfTypeId(countType)); + cudf_velox::veloxToCudfDataType(rowType.childAt(0)); // sum auto const aggRequest = @@ -500,8 +797,7 @@ struct MeanAggregator : cudf_velox::CudfHashAggregation::Aggregator { countCol, *countAggRequest, countCol.type(), stream, get_temp_mr()); // divide the sums by the counts - auto const cudfOutputType = - cudf::data_type(cudf_velox::veloxToCudfTypeId(outputType)); + auto const cudfOutputType = cudf_velox::veloxToCudfDataType(outputType); return cudf::binary_operation( *sumResultCol, *countResultScalar, @@ -547,7 +843,8 @@ struct ApproxDistinctAggregator : cudf_velox::CudfHashAggregation::Aggregator { void addGroupbyRequest( cudf::table_view const& tbl, - std::vector& requests) override { + std::vector& requests, + rmm::cuda_stream_view stream) override { VELOX_UNSUPPORTED( "approx_distinct is not supported as a group aggregation"); } @@ -742,10 +1039,17 @@ std::unique_ptr createAggregator( uint32_t inputIndex, VectorPtr constant, bool isGlobal, - const TypePtr& resultType) { + const TypePtr& resultType, + const std::vector& rawInputTypes = {}) { auto const& kind = aggregate.call->name(); auto prefix = cudf_velox::CudfConfig::getInstance().functionNamePrefix; if (kind.rfind(prefix + "sum", 0) == 0) { + bool isDecimalInput = + rawInputTypes.size() == 1 && rawInputTypes[0]->isDecimal(); + if (isDecimalInput) { + return std::make_unique( + step, inputIndex, constant, isGlobal, resultType, false); + } return std::make_unique( step, inputIndex, constant, isGlobal, resultType); } else if (kind.rfind(prefix + "count", 0) == 0) { @@ -758,6 +1062,12 @@ std::unique_ptr createAggregator( return std::make_unique( step, inputIndex, constant, isGlobal, resultType); } else if (kind.rfind(prefix + "avg", 0) == 0) { + bool isDecimalInput = + rawInputTypes.size() == 1 && rawInputTypes[0]->isDecimal(); + if (isDecimalInput) { + return std::make_unique( + step, inputIndex, constant, isGlobal, resultType, true); + } return std::make_unique( step, inputIndex, constant, isGlobal, resultType); } else if (kind.rfind(prefix + "approx_distinct", 0) == 0) { @@ -911,7 +1221,14 @@ auto toAggregators( : outputType->childAt(numKeys + i); aggregators.push_back(createAggregator( - companionStep, aggregate, inputIndex, constant, isGlobal, resultType)); + companionStep, + aggregate, + kind, + inputIndex, + constant, + isGlobal, + resultType, + aggregate.rawInputTypes)); } return aggregators; } @@ -1292,7 +1609,7 @@ CudfVectorPtr CudfHashAggregation::doGroupByAggregation( std::vector requests; for (auto& aggregator : aggregators) { - aggregator->addGroupbyRequest(tableView, requests); + aggregator->addGroupbyRequest(tableView, requests, stream); } auto [groupKeys, results] = @@ -1559,6 +1876,38 @@ bool registerStepAwareBuiltinAggregationFunctions(const std::string& prefix) { .argumentType("double") .build()}; + // Decimal sum signatures. + auto decimalSumSingle = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("decimal(38, a_scale)") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalSumPartial = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("varbinary") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalSumFinal = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_scale") + .returnType("decimal(38, a_scale)") + .argumentType("varbinary") + .build()}; + auto decimalSumIntermediate = + std::vector{FunctionSignatureBuilder() + .returnType("varbinary") + .argumentType("varbinary") + .build()}; + + sumSingleSignatures.insert( + sumSingleSignatures.end(), + decimalSumSingle.begin(), + decimalSumSingle.end()); + registerAggregationFunctionForStep( prefix + "sum", core::AggregationNode::Step::kSingle, @@ -1585,6 +1934,12 @@ bool registerStepAwareBuiltinAggregationFunctions(const std::string& prefix) { .returnType("double") .argumentType("double") .build()}; + + sumPartialSignatures.insert( + sumPartialSignatures.end(), + decimalSumPartial.begin(), + decimalSumPartial.end()); + registerAggregationFunctionForStep( prefix + "sum", core::AggregationNode::Step::kPartial, @@ -1600,14 +1955,23 @@ bool registerStepAwareBuiltinAggregationFunctions(const std::string& prefix) { .argumentType("double") .build()}; + auto sumFinalSignatures = sumFinalIntermediateSignatures; + sumFinalSignatures.insert( + sumFinalSignatures.end(), decimalSumFinal.begin(), decimalSumFinal.end()); + registerAggregationFunctionForStep( - prefix + "sum", - core::AggregationNode::Step::kFinal, - sumFinalIntermediateSignatures); + prefix + "sum", core::AggregationNode::Step::kFinal, sumFinalSignatures); + + auto sumIntermediateSignatures = sumFinalIntermediateSignatures; + sumIntermediateSignatures.insert( + sumIntermediateSignatures.end(), + decimalSumIntermediate.begin(), + decimalSumIntermediate.end()); + registerAggregationFunctionForStep( prefix + "sum", core::AggregationNode::Step::kIntermediate, - sumFinalIntermediateSignatures); + sumIntermediateSignatures); // Register count function (split by aggregation step) auto countSinglePartialSignatures = std::vector{ @@ -1697,6 +2061,12 @@ bool registerStepAwareBuiltinAggregationFunctions(const std::string& prefix) { FunctionSignatureBuilder() .returnType("varchar") .argumentType("varchar") + .build(), + FunctionSignatureBuilder() + .integerVariable("p") + .integerVariable("s") + .returnType("decimal(p,s)") + .argumentType("decimal(p,s)") .build()}; registerAggregationFunctionForStep( @@ -1742,6 +2112,40 @@ bool registerStepAwareBuiltinAggregationFunctions(const std::string& prefix) { .returnType("double") .argumentType("double") .build()}; + + // Decimal avg signatures. + auto decimalAvgSingle = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("decimal(a_precision, a_scale)") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalAvgPartial = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("varbinary") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalAvgFinal = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("decimal(a_precision, a_scale)") + .argumentType("varbinary") + .build()}; + auto decimalAvgIntermediate = + std::vector{FunctionSignatureBuilder() + .returnType("varbinary") + .argumentType("varbinary") + .build()}; + + avgSingleSignatures.insert( + avgSingleSignatures.end(), + decimalAvgSingle.begin(), + decimalAvgSingle.end()); + registerAggregationFunctionForStep( prefix + "avg", core::AggregationNode::Step::kSingle, @@ -1768,17 +2172,28 @@ bool registerStepAwareBuiltinAggregationFunctions(const std::string& prefix) { .returnType("row(double,bigint)") .argumentType("double") .build()}; + + avgPartialSignatures.insert( + avgPartialSignatures.end(), + decimalAvgPartial.begin(), + decimalAvgPartial.end()); + registerAggregationFunctionForStep( prefix + "avg", core::AggregationNode::Step::kPartial, avgPartialSignatures); // Final step: avg(row(double, bigint)) -> double - auto avgFinalSignatures = std::vector{ + auto avgFinalIntermediateSignatures = std::vector{ FunctionSignatureBuilder() .returnType("double") .argumentType("row(double,bigint)") .build()}; + + auto avgFinalSignatures = avgFinalIntermediateSignatures; + avgFinalSignatures.insert( + avgFinalSignatures.end(), decimalAvgFinal.begin(), decimalAvgFinal.end()); + registerAggregationFunctionForStep( prefix + "avg", core::AggregationNode::Step::kFinal, avgFinalSignatures); @@ -1789,6 +2204,16 @@ bool registerStepAwareBuiltinAggregationFunctions(const std::string& prefix) { .returnType("row(double,bigint)") .argumentType("row(double,bigint)") .build()}; + + // WHY DOES SUM NOT HAVE THE EQUIVALENT OF THE ABOVE? + // THE ABOVE THEN CLASHES WITH BELOW + // @mattgara HELP! :) + // auto avgIntermediateSignatures = avgFinalIntermediateSignatures; + avgIntermediateSignatures.insert( + avgIntermediateSignatures.end(), + decimalAvgIntermediate.begin(), + decimalAvgIntermediate.end()); + registerAggregationFunctionForStep( prefix + "avg", core::AggregationNode::Step::kIntermediate, diff --git a/velox/experimental/cudf/exec/CudfHashAggregation.h b/velox/experimental/cudf/exec/CudfHashAggregation.h index ec39b866560..4081bd4a5bc 100644 --- a/velox/experimental/cudf/exec/CudfHashAggregation.h +++ b/velox/experimental/cudf/exec/CudfHashAggregation.h @@ -42,7 +42,8 @@ class CudfHashAggregation : public CudfOperatorBase { virtual void addGroupbyRequest( cudf::table_view const& tbl, - std::vector& requests) = 0; + std::vector& requests, + rmm::cuda_stream_view stream) = 0; virtual std::unique_ptr doReduce( cudf::table_view const& input, diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cu b/velox/experimental/cudf/exec/DecimalAggregationKernels.cu new file mode 100644 index 00000000000..27d74e36b15 --- /dev/null +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cu @@ -0,0 +1,432 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/experimental/cudf/exec/DecimalAggregationKernels.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace facebook::velox::cudf_velox { +namespace { + +constexpr int32_t kStateSize = 32; + +struct DecimalSumStateDevice { + int64_t count; + int64_t overflow; + uint64_t lower; + int64_t upper; +}; + +static_assert(sizeof(DecimalSumStateDevice) == kStateSize); + +__device__ __forceinline__ void +splitToWords(int64_t value, int64_t& upper, uint64_t& lower) { + lower = static_cast(value); + upper = value < 0 ? -1 : 0; +} + +__device__ __forceinline__ void +splitToWords(__int128_t value, int64_t& upper, uint64_t& lower) { + lower = static_cast(value); + upper = static_cast(value >> 64); +} + +template +__global__ void fillOffsetsKernel(OffsetT* offsets, int32_t numRows) { + int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx <= numRows) { + int64_t offset = static_cast(idx) * kStateSize; + offsets[idx] = static_cast(offset); + } +} + +template +__global__ void packStateKernel( + const SumT* sums, + const int64_t* counts, + const OffsetT* offsets, + uint8_t* chars, + int32_t numRows) { + int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx >= numRows) { + return; + } + int64_t offset = static_cast(offsets[idx]); + auto* state = reinterpret_cast(chars + offset); + int64_t upper; + uint64_t lower; + splitToWords(sums[idx], upper, lower); + state->count = counts[idx]; + state->overflow = 0; + state->lower = lower; + state->upper = upper; +} + +template +__global__ void unpackStateKernel( + const OffsetT* offsets, + const uint8_t* chars, + __int128_t* sums, + int64_t* counts, + int32_t numRows) { + int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx >= numRows) { + return; + } + int64_t offset = static_cast(offsets[idx]); + auto* state = reinterpret_cast(chars + offset); + counts[idx] = state->count; + sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; +} + +template +__global__ void avgRoundKernel( + const SumT* sums, + const int64_t* counts, + SumT* out, + int32_t numRows) { + int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx >= numRows) { + return; + } + auto count = counts[idx]; + if (count == 0) { + out[idx] = SumT{0}; + return; + } + auto sum = sums[idx]; + SumT absSum = sum < 0 ? -sum : sum; + SumT half = static_cast(count / 2); + SumT rounded = (absSum + half) / static_cast(count); + out[idx] = sum < 0 ? -rounded : rounded; +} + +struct StateValidPredicate { + cudf::column_device_view sum; + cudf::column_device_view count; + + __device__ bool operator()(cudf::size_type idx) const { + if (sum.is_null(idx) || count.is_null(idx)) { + return false; + } + return count.element(idx) != 0; + } +}; + +std::pair buildStateValidityMask( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + auto numRows = sumCol.size(); + if (numRows == 0) { + return {rmm::device_buffer{}, 0}; + } + auto sumDeviceView = cudf::column_device_view::create(sumCol, stream); + auto countDeviceView = cudf::column_device_view::create(countCol, stream); + StateValidPredicate pred{*sumDeviceView, *countDeviceView}; + auto begin = thrust::make_counting_iterator(0); + auto end = begin + numRows; + return cudf::detail::valid_if(begin, end, pred, stream, mr); +} + +} // namespace + +DecimalSumStateColumns deserializeDecimalSumStateWithCount( + const cudf::column_view& stateCol, + int32_t scale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + CUDF_EXPECTS( + stateCol.type().id() == cudf::type_id::STRING, + "Decimal sum state requires STRING/VARBINARY column"); + auto numRows = stateCol.size(); + if (numRows == 0) { + DecimalSumStateColumns empty; + empty.sum = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::DECIMAL128, -scale}, + 0, + cudf::mask_state::UNALLOCATED, + stream); + empty.count = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::INT64}, + 0, + cudf::mask_state::UNALLOCATED, + stream); + return empty; + } + + // For fully-null state columns there is nothing to deserialize. Avoid + // launching unpack kernels over string payload buffers that may be empty. + if (stateCol.nullable() && stateCol.null_count() == numRows) { + DecimalSumStateColumns allNull; + allNull.sum = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::DECIMAL128, -scale}, + numRows, + cudf::mask_state::ALL_NULL, + stream); + allNull.count = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::INT64}, + numRows, + cudf::mask_state::ALL_NULL, + stream); + return allNull; + } + + cudf::strings_column_view strings(stateCol); + numRows = strings.size(); + + auto offsetsView = strings.offsets(); + auto offsetsType = offsetsView.type().id(); + auto charsPtr = reinterpret_cast(strings.chars_begin(stream)); + + auto sumCol = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::DECIMAL128, -scale}, + numRows, + cudf::mask_state::UNALLOCATED, + stream); + auto countCol = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::INT64}, + numRows, + cudf::mask_state::UNALLOCATED, + stream); + + auto sumView = sumCol->mutable_view(); + auto countView = countCol->mutable_view(); + + if (numRows > 0) { + int32_t blockSize = 256; + int32_t gridSize = (numRows + blockSize - 1) / blockSize; + if (offsetsType == cudf::type_id::INT64) { + auto offsetsCol = offsetsView.data(); + unpackStateKernel<<>>( + offsetsCol, + charsPtr, + sumView.data<__int128_t>(), + countView.data(), + numRows); + } else { + CUDF_EXPECTS( + offsetsType == cudf::type_id::INT32, + "Decimal sum state requires INT32 or INT64 offsets"); + auto offsetsCol = offsetsView.data(); + unpackStateKernel<<>>( + offsetsCol, + charsPtr, + sumView.data<__int128_t>(), + countView.data(), + numRows); + } + CUDF_CUDA_TRY(cudaGetLastError()); + } + + if (stateCol.nullable()) { + auto nullMask = cudf::copy_bitmask(stateCol, stream, mr); + auto nullCount = stateCol.null_count(); + sumCol->set_null_mask(std::move(nullMask), nullCount); + auto countMask = cudf::copy_bitmask(stateCol, stream, mr); + countCol->set_null_mask(std::move(countMask), nullCount); + } + + DecimalSumStateColumns result; + result.sum = std::move(sumCol); + result.count = std::move(countCol); + return result; +} + +std::unique_ptr deserializeDecimalSumState( + const cudf::column_view& stateCol, + int32_t scale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + auto decoded = + deserializeDecimalSumStateWithCount(stateCol, scale, stream, mr); + return std::move(decoded.sum); +} + +std::unique_ptr serializeDecimalSumState( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + CUDF_EXPECTS( + countCol.type().id() == cudf::type_id::INT64, + "Decimal sum state requires INT64 count column"); + auto numRows = sumCol.size(); + CUDF_EXPECTS( + numRows == countCol.size(), + "Decimal sum state requires sum and count to be same size"); + CUDF_EXPECTS( + numRows <= std::numeric_limits::max(), + "Too many rows to serialize decimal sum state"); + + auto const charsBytes = static_cast(numRows) * kStateSize; + auto const threshold = cudf::strings::get_offset64_threshold(); + auto const useLargeOffsets = charsBytes >= threshold; + CUDF_EXPECTS( + !useLargeOffsets || cudf::strings::is_large_strings_enabled(), + "Size of output exceeds the column size limit", + std::overflow_error); + + auto const offsetsType = + useLargeOffsets ? cudf::type_id::INT64 : cudf::type_id::INT32; + auto offsetsCol = cudf::make_fixed_width_column( + cudf::data_type{offsetsType}, + numRows + 1, + cudf::mask_state::UNALLOCATED, + stream); + auto offsetsView = offsetsCol->mutable_view(); + + rmm::device_buffer charsBuf( + static_cast(numRows) * kStateSize, stream); + + int32_t blockSize = 256; + int32_t offsetGridSize = (numRows + 1 + blockSize - 1) / blockSize; + if (useLargeOffsets) { + fillOffsetsKernel + <<>>( + offsetsView.data(), numRows); + } else { + fillOffsetsKernel + <<>>( + offsetsView.data(), numRows); + } + CUDF_CUDA_TRY(cudaGetLastError()); + + if (numRows > 0) { + int32_t gridSize = (numRows + blockSize - 1) / blockSize; + auto charsPtr = reinterpret_cast(charsBuf.data()); + if (useLargeOffsets) { + auto offsetsPtr = offsetsView.data(); + if (sumCol.type().id() == cudf::type_id::DECIMAL64) { + packStateKernel + <<>>( + sumCol.data(), + countCol.data(), + offsetsPtr, + charsPtr, + numRows); + } else { + CUDF_EXPECTS( + sumCol.type().id() == cudf::type_id::DECIMAL128, + "Unsupported decimal sum column type"); + packStateKernel<__int128_t, int64_t> + <<>>( + sumCol.data<__int128_t>(), + countCol.data(), + offsetsPtr, + charsPtr, + numRows); + } + } else { + auto offsetsPtr = offsetsView.data(); + if (sumCol.type().id() == cudf::type_id::DECIMAL64) { + packStateKernel + <<>>( + sumCol.data(), + countCol.data(), + offsetsPtr, + charsPtr, + numRows); + } else { + CUDF_EXPECTS( + sumCol.type().id() == cudf::type_id::DECIMAL128, + "Unsupported decimal sum column type"); + packStateKernel<__int128_t, int32_t> + <<>>( + sumCol.data<__int128_t>(), + countCol.data(), + offsetsPtr, + charsPtr, + numRows); + } + } + CUDF_CUDA_TRY(cudaGetLastError()); + } + + auto [nullMask, nullCount] = + buildStateValidityMask(sumCol, countCol, stream, mr); + return cudf::make_strings_column( + static_cast(numRows), + std::move(offsetsCol), + std::move(charsBuf), + nullCount, + std::move(nullMask)); +} + +std::unique_ptr computeDecimalAverage( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + CUDF_EXPECTS( + countCol.type().id() == cudf::type_id::INT64, + "Decimal average requires INT64 count column"); + CUDF_EXPECTS( + sumCol.type().id() == cudf::type_id::DECIMAL64 || + sumCol.type().id() == cudf::type_id::DECIMAL128, + "Decimal average requires DECIMAL64 or DECIMAL128 sum column"); + CUDF_EXPECTS( + sumCol.size() == countCol.size(), + "Decimal average requires sum and count to be same size"); + + auto numRows = sumCol.size(); + auto out = cudf::make_fixed_width_column( + sumCol.type(), numRows, cudf::mask_state::UNALLOCATED, stream); + + if (numRows > 0) { + int32_t blockSize = 256; + int32_t gridSize = (numRows + blockSize - 1) / blockSize; + if (sumCol.type().id() == cudf::type_id::DECIMAL64) { + avgRoundKernel<<>>( + sumCol.data(), + countCol.data(), + out->mutable_view().data(), + numRows); + } else { + avgRoundKernel<<>>( + sumCol.data<__int128_t>(), + countCol.data(), + out->mutable_view().data<__int128_t>(), + numRows); + } + CUDF_CUDA_TRY(cudaGetLastError()); + } + + auto [nullMask, nullCount] = + buildStateValidityMask(sumCol, countCol, stream, mr); + if (nullCount > 0) { + out->set_null_mask(std::move(nullMask), nullCount); + } else if (nullMask.size() > 0) { + out->set_null_mask(std::move(nullMask), 0); + } + return out; +} + +} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.h b/velox/experimental/cudf/exec/DecimalAggregationKernels.h new file mode 100644 index 00000000000..620194249f2 --- /dev/null +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 +#include + +#include + +#include + +namespace facebook::velox::cudf_velox { + +struct DecimalSumStateColumns { + std::unique_ptr sum; + std::unique_ptr count; +}; + +DecimalSumStateColumns deserializeDecimalSumStateWithCount( + const cudf::column_view& stateCol, + int32_t scale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); + +std::unique_ptr deserializeDecimalSumState( + const cudf::column_view& stateCol, + int32_t scale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); + +std::unique_ptr serializeDecimalSumState( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); + +std::unique_ptr computeDecimalAverage( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); + +} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/exec/VeloxCudfInterop.cpp b/velox/experimental/cudf/exec/VeloxCudfInterop.cpp index 0a73bfcf34c..ab6418443d0 100644 --- a/velox/experimental/cudf/exec/VeloxCudfInterop.cpp +++ b/velox/experimental/cudf/exec/VeloxCudfInterop.cpp @@ -40,12 +40,6 @@ namespace facebook::velox::cudf_velox { -cudf::type_id veloxToCudfTypeId(const TypePtr& type) { - // Legacy helper retained for compatibility. Note: returning cudf::type_id - // discards decimal scale; prefer veloxToCudfDataType when scale matters. - return veloxToCudfDataType(type).id(); -} - cudf::data_type veloxToCudfDataType(const TypePtr& type) { switch (type->kind()) { case TypeKind::BOOLEAN: diff --git a/velox/experimental/cudf/exec/VeloxCudfInterop.h b/velox/experimental/cudf/exec/VeloxCudfInterop.h index 47f638a8b6a..03fe48e5494 100644 --- a/velox/experimental/cudf/exec/VeloxCudfInterop.h +++ b/velox/experimental/cudf/exec/VeloxCudfInterop.h @@ -26,8 +26,6 @@ namespace facebook::velox::cudf_velox { -cudf::type_id veloxToCudfTypeId(const TypePtr& type); - cudf::data_type veloxToCudfDataType(const TypePtr& type); namespace with_arrow { diff --git a/velox/experimental/cudf/tests/CMakeLists.txt b/velox/experimental/cudf/tests/CMakeLists.txt index 78c9ef62ff1..0b620c8dabf 100644 --- a/velox/experimental/cudf/tests/CMakeLists.txt +++ b/velox/experimental/cudf/tests/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(velox_cudf_config_test Main.cpp ConfigTest.cpp) add_executable(velox_cudf_enforce_single_row_test Main.cpp EnforceSingleRowTest.cpp) add_executable(velox_cudf_expression_selection_test Main.cpp ExpressionEvaluatorSelectionTest.cpp) add_executable(velox_cudf_decimal_expression_test Main.cpp DecimalExpressionTest.cpp) +add_executable(velox_cudf_decimal_aggregation_test Main.cpp DecimalAggregationTest.cpp) add_executable(velox_cudf_filter_project_test Main.cpp FilterProjectTest.cpp) add_executable(velox_cudf_hash_join_test HashJoinTest.cpp Main.cpp) add_executable(velox_cudf_limit_test Main.cpp LimitTest.cpp) @@ -78,6 +79,12 @@ add_test( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) +add_test( + NAME velox_cudf_decimal_aggregation_test + COMMAND velox_cudf_decimal_aggregation_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + add_test( NAME velox_cudf_filter_project_test COMMAND velox_cudf_filter_project_test @@ -180,6 +187,7 @@ set_tests_properties( PROPERTIES LABELS cuda_driver TIMEOUT 3000 ) set_tests_properties(velox_cudf_decimal_expression_test PROPERTIES LABELS cuda_driver TIMEOUT 3000) +set_tests_properties(velox_cudf_decimal_aggregation_test PROPERTIES LABELS cuda_driver TIMEOUT 3000) set_tests_properties(velox_cudf_filter_project_test PROPERTIES LABELS cuda_driver TIMEOUT 3000) set_tests_properties(velox_cudf_hash_join_test PROPERTIES LABELS cuda_driver TIMEOUT 3000) set_tests_properties(velox_cudf_limit_test PROPERTIES LABELS cuda_driver TIMEOUT 3000) @@ -260,6 +268,17 @@ target_link_libraries( gtest_main ) +target_link_libraries( + velox_cudf_decimal_aggregation_test + velox_cudf_exec + velox_exec + velox_exec_test_lib + velox_functions_test_lib + velox_test_util + gtest + gtest_main +) + target_link_libraries( velox_cudf_filter_project_test velox_cudf_exec diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp new file mode 100644 index 00000000000..6734025e752 --- /dev/null +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -0,0 +1,1407 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/experimental/cudf/CudfConfig.h" +#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" +#include "velox/experimental/cudf/exec/ToCudf.h" +#include "velox/experimental/cudf/exec/VeloxCudfInterop.h" + +#include "velox/common/base/tests/GTestUtils.h" +#include "velox/common/file/FileSystems.h" +#include "velox/exec/tests/utils/AssertQueryBuilder.h" +#include "velox/exec/tests/utils/OperatorTestBase.h" +#include "velox/exec/tests/utils/PlanBuilder.h" +#include "velox/functions/prestosql/aggregates/RegisterAggregateFunctions.h" +#include "velox/functions/prestosql/registration/RegistrationFunctions.h" +#include "velox/parse/TypeResolver.h" +#include "velox/type/DecimalUtil.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace facebook::velox::cudf_velox { +namespace { + +int64_t computeAvgRaw(const std::vector& values) { + int128_t sum = 0; + for (auto value : values) { + sum += value; + } + int128_t avg = 0; + facebook::velox::DecimalUtil::computeAverage(avg, sum, values.size(), 0); + return static_cast(avg); +} + +constexpr int kBitsPerWord = 8 * sizeof(cudf::bitmask_type); + +std::pair makeNullMask( + const std::vector& valid, + rmm::cuda_stream_view stream) { + auto numBits = static_cast(valid.size()); + if (numBits == 0) { + return {rmm::device_buffer{}, 0}; + } + auto maskBytes = cudf::bitmask_allocation_size_bytes(numBits); + auto numWords = maskBytes / sizeof(cudf::bitmask_type); + std::vector host(numWords, 0); + cudf::size_type nullCount = 0; + for (cudf::size_type i = 0; i < numBits; ++i) { + if (valid[i]) { + auto word = i / kBitsPerWord; + auto bit = i % kBitsPerWord; + host[word] |= (cudf::bitmask_type{1} << bit); + } else { + ++nullCount; + } + } + rmm::device_buffer mask(maskBytes, stream); + if (!host.empty()) { + auto status = cudaMemcpyAsync( + mask.data(), + host.data(), + host.size() * sizeof(cudf::bitmask_type), + cudaMemcpyHostToDevice, + stream.value()); + VELOX_CHECK_EQ(0, static_cast(status)); + stream.synchronize(); + } + return {std::move(mask), nullCount}; +} + +class ScopedEnvVar { + public: + ScopedEnvVar(const char* key, const char* value) : key_(key) { + const char* existing = std::getenv(key); + if (existing) { + oldValue_ = std::string(existing); + } + if (value) { + setenv(key, value, 1); + } else { + unsetenv(key); + } + } + + ~ScopedEnvVar() { + if (oldValue_) { + setenv(key_.c_str(), oldValue_->c_str(), 1); + } else { + unsetenv(key_.c_str()); + } + } + + private: + std::string key_; + std::optional oldValue_; +}; + +template +std::unique_ptr makeFixedWidthColumn( + cudf::data_type type, + const std::vector& values, + const std::vector* valid, + rmm::cuda_stream_view stream) { + auto col = cudf::make_fixed_width_column( + type, + static_cast(values.size()), + cudf::mask_state::UNALLOCATED, + stream); + if (!values.empty()) { + auto status = cudaMemcpyAsync( + col->mutable_view().data(), + values.data(), + values.size() * sizeof(T), + cudaMemcpyHostToDevice, + stream.value()); + VELOX_CHECK_EQ(0, static_cast(status)); + stream.synchronize(); + } + if (valid) { + auto [mask, nullCount] = makeNullMask(*valid, stream); + col->set_null_mask(std::move(mask), nullCount); + } + return col; +} + +template +std::unique_ptr makeDecimalColumn( + const std::vector& values, + int32_t scale, + const std::vector* valid, + rmm::cuda_stream_view stream) { + cudf::type_id typeId = std::is_same_v ? cudf::type_id::DECIMAL64 + : cudf::type_id::DECIMAL128; + cudf::data_type type{typeId, -scale}; + return makeFixedWidthColumn(type, values, valid, stream); +} + +std::unique_ptr makeInt64Column( + const std::vector& values, + const std::vector* valid, + rmm::cuda_stream_view stream) { + return makeFixedWidthColumn( + cudf::data_type{cudf::type_id::INT64}, values, valid, stream); +} + +template +std::vector copyColumnData( + const cudf::column_view& view, + rmm::cuda_stream_view stream) { + std::vector host(view.size()); + if (view.size() == 0) { + return host; + } + auto status = cudaMemcpyAsync( + host.data(), + view.data(), + view.size() * sizeof(T), + cudaMemcpyDeviceToHost, + stream.value()); + VELOX_CHECK_EQ(0, static_cast(status)); + stream.synchronize(); + return host; +} + +std::vector copyNullMask( + const cudf::column_view& view, + rmm::cuda_stream_view stream) { + auto numWords = cudf::num_bitmask_words(view.size()); + std::vector host(numWords, 0); + if (!view.nullable() || numWords == 0) { + return host; + } + auto status = cudaMemcpyAsync( + host.data(), + view.null_mask(), + host.size() * sizeof(cudf::bitmask_type), + cudaMemcpyDeviceToHost, + stream.value()); + VELOX_CHECK_EQ(0, static_cast(status)); + stream.synchronize(); + return host; +} + +bool isValidAt(const std::vector& mask, size_t idx) { + if (mask.empty()) { + return true; + } + auto word = idx / kBitsPerWord; + auto bit = idx % kBitsPerWord; + return (mask[word] >> bit) & 1; +} + +class CudfDecimalTest : public exec::test::OperatorTestBase { + protected: + void SetUp() override { + exec::test::OperatorTestBase::SetUp(); + filesystems::registerLocalFileSystem(); + parse::registerTypeResolver(); + functions::prestosql::registerAllScalarFunctions(); + aggregate::prestosql::registerAllAggregateFunctions(); + CudfConfig::getInstance().allowCpuFallback = false; + // Ensure a CUDA device is selected and initialized (RMM asserts otherwise). + int deviceCount = 0; + auto status = cudaGetDeviceCount(&deviceCount); + if (status != cudaSuccess) { + GTEST_SKIP() << "cudaGetDeviceCount failed: " << static_cast(status) + << " (" << cudaGetErrorString(status) << ")"; + } + if (deviceCount == 0) { + GTEST_SKIP() << "No CUDA devices visible (check CUDA_VISIBLE_DEVICES)"; + } + VELOX_CHECK_EQ(0, static_cast(cudaSetDevice(0))); + VELOX_CHECK_EQ(0, static_cast(cudaFree(nullptr))); + registerCudf(); + } + + void TearDown() override { + unregisterCudf(); + exec::test::OperatorTestBase::TearDown(); + } +}; + +TEST_F(CudfDecimalTest, DISABLED_decimalAvgAndSumTimesDouble) { + auto rowType = ROW({ + {"l_quantity", DECIMAL(15, 2)}, + }); + + // Values chosen to keep the AVG and SUM exact in double. + auto input = makeRowVector( + {"l_quantity"}, + {makeFlatVector( + {125, 250, 375, 400}, // 1.25, 2.50, 3.75, 4.00 + DECIMAL(15, 2))}); + + std::vector vectors = {input}; + createDuckDbTable(vectors); + + // Force CPU-only path to validate this fails without cuDF involvement. + unregisterCudf(); + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .project({"l_quantity * 2.0 AS qty2"}) + .singleAggregation( + {}, {"avg(qty2) AS avg_qty", "sum(qty2) AS sum_qty"}) + .planNode(); + + facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) + .assertResults( + "SELECT avg(l_quantity * 2.0) AS avg_qty, " + "sum(l_quantity * 2.0) AS sum_qty " + "FROM tmp"); +} + +TEST_F(CudfDecimalTest, decimalAvgDecimalInput) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeFlatVector( + {100, 200, 300, 400}, // 1.00, 2.00, 3.00, 4.00 + DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({}, {"avg(d) AS avg_d"}) + .planNode(); + + auto expected = makeRowVector( + {"avg_d"}, {makeFlatVector({250}, DECIMAL(12, 2))}); // 2.50 + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgDecimalInputRounds) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + // Sum = 1.60, count = 7 => 0.22857..., rounds to 0.23 at scale 2. + std::vector rawValues = {100, 10, 10, 10, 10, 10, 10}; + auto input = makeRowVector( + {"d"}, {makeFlatVector(rawValues, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({}, {"avg(d) AS avg_d"}) + .planNode(); + + auto expected = makeRowVector( + {"avg_d"}, + {makeFlatVector({computeAvgRaw(rawValues)}, DECIMAL(12, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgPartialFinalVarbinaryRounds) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + std::vector keys = {1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3}; + std::vector values = {100, 10, 10, 10, 10, 10, 10, 100, 1, -100, -1}; + + auto input = makeRowVector( + {"k", "d"}, + { + makeFlatVector(keys), + makeFlatVector(values, DECIMAL(12, 2)), + }); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"avg(d) AS a"}) + .finalAggregation() + .orderBy({"k"}, false) + .planNode(); + + std::vector>> groups = { + {1, {100, 10, 10, 10, 10, 10, 10}}, + {2, {100, 1}}, + {3, {-100, -1}}, + }; + + auto expected = makeRowVector( + {"k", "a"}, + { + makeFlatVector({1, 2, 3}), + makeFlatVector( + {computeAvgRaw(groups[0].second), + computeAvgRaw(groups[1].second), + computeAvgRaw(groups[2].second)}, + DECIMAL(12, 2)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgIntermediateVarbinaryRounds) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 1, 2, 3}), + makeFlatVector({100, 10, 100, -100}, DECIMAL(12, 2)), + }); + auto input2 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 1, 1, 1, 1, 2, 3}), + makeFlatVector({10, 10, 10, 10, 10, 1, -1}, DECIMAL(12, 2)), + }); + + std::vector vectors = {input1, input2}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"avg(d) AS a"}) + .intermediateAggregation() + .finalAggregation() + .orderBy({"k"}, false) + .planNode(); + + std::vector>> groups = { + {1, {100, 10, 10, 10, 10, 10, 10}}, + {2, {100, 1}}, + {3, {-100, -1}}, + }; + + auto expected = makeRowVector( + {"k", "a"}, + { + makeFlatVector({1, 2, 3}), + makeFlatVector( + {computeAvgRaw(groups[0].second), + computeAvgRaw(groups[1].second), + computeAvgRaw(groups[2].second)}, + DECIMAL(12, 2)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgGlobalPartialFinalVarbinaryRounds) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"d"}, {makeFlatVector({100, 10, 10}, DECIMAL(12, 2))}); + auto input2 = makeRowVector( + {"d"}, {makeFlatVector({10, 10, 10, 10}, DECIMAL(12, 2))}); + + std::vector vectors = {input1, input2}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"avg(d) AS a"}) + .finalAggregation() + .planNode(); + + std::vector allValues = {100, 10, 10, 10, 10, 10, 10}; + auto expected = makeRowVector( + {"a"}, + {makeFlatVector({computeAvgRaw(allValues)}, DECIMAL(12, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgGlobalIntermediateVarbinaryRounds) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"d"}, {makeFlatVector({100, 10, 10}, DECIMAL(12, 2))}); + auto input2 = makeRowVector( + {"d"}, {makeFlatVector({10, 10, 10, 10}, DECIMAL(12, 2))}); + + std::vector vectors = {input1, input2}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"avg(d) AS a"}) + .intermediateAggregation() + .finalAggregation() + .planNode(); + + std::vector allValues = {100, 10, 10, 10, 10, 10, 10}; + auto expected = makeRowVector( + {"a"}, + {makeFlatVector({computeAvgRaw(allValues)}, DECIMAL(12, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgGlobalSingleRounds) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeFlatVector({100, 10, 10, 10, 10, 10, 10}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({}, {"avg(d) AS a"}) + .planNode(); + + std::vector allValues = {100, 10, 10, 10, 10, 10, 10}; + auto expected = makeRowVector( + {"a"}, + {makeFlatVector({computeAvgRaw(allValues)}, DECIMAL(12, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgGlobalSingleAllNulls) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeNullableFlatVector( + {std::nullopt, std::nullopt, std::nullopt}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({}, {"avg(d) AS a"}) + .planNode(); + + auto expected = makeRowVector( + {"a"}, {makeNullableFlatVector({std::nullopt}, DECIMAL(12, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgGlobalPartialFinalVarbinaryAllNulls) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeNullableFlatVector( + {std::nullopt, std::nullopt, std::nullopt}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"avg(d) AS a"}) + .finalAggregation() + .planNode(); + + auto expected = makeRowVector( + {"a"}, {makeNullableFlatVector({std::nullopt}, DECIMAL(12, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgGlobalIntermediateVarbinaryAllNulls) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeNullableFlatVector( + {std::nullopt, std::nullopt, std::nullopt}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"avg(d) AS a"}) + .intermediateAggregation() + .finalAggregation() + .planNode(); + + auto expected = makeRowVector( + {"a"}, {makeNullableFlatVector({std::nullopt}, DECIMAL(12, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgPartialFinalVarbinaryNullGroup) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 1, 2, 2, 3, 3}), + makeNullableFlatVector( + {100, 200, std::nullopt, std::nullopt, 400, std::nullopt}, + DECIMAL(12, 2)), + }); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"avg(d) AS a"}) + .finalAggregation() + .orderBy({"k"}, false) + .planNode(); + + auto expected = makeRowVector( + {"k", "a"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {150, std::nullopt, 400}, DECIMAL(12, 2)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgIntermediateVarbinaryNullGroup) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {100, std::nullopt, 400}, DECIMAL(12, 2)), + }); + auto input2 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {200, std::nullopt, std::nullopt}, DECIMAL(12, 2)), + }); + + std::vector vectors = {input1, input2}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"avg(d) AS a"}) + .intermediateAggregation() + .finalAggregation() + .orderBy({"k"}, false) + .planNode(); + + auto expected = makeRowVector( + {"k", "a"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {150, std::nullopt, 400}, DECIMAL(12, 2)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalSumPartialFinalVarbinary) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 1, 2, 2, 2}), + makeFlatVector( + {12345, -2500, 10000, 200, -300}, DECIMAL(12, 2)), + }); + + std::vector vectors = {input}; + createDuckDbTable(vectors); + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"sum(d) AS s"}) + .finalAggregation() + .planNode(); + + facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) + .assertResults("SELECT k, sum(d) AS s FROM tmp GROUP BY k"); +} + +TEST_F(CudfDecimalTest, decimalPartialSumVarbinaryToVeloxRoundTrip) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, {makeFlatVector({100, 200, 300}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"sum(d) AS s"}) + .planNode(); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + VELOX_CHECK_NOT_NULL(result); + ASSERT_GT(result->size(), 0); + ASSERT_EQ(result->childAt(0)->type()->kind(), TypeKind::VARBINARY); +} + +TEST_F(CudfDecimalTest, decimalSumPartialFinalEmptyInput) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 2, 3}), + makeFlatVector({100, 200, 300}, DECIMAL(12, 2)), + }); + + std::vector vectors = {input}; + createDuckDbTable(vectors); + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .filter("k < 0") + .partialAggregation({"k"}, {"sum(d) AS s"}) + .finalAggregation() + .planNode(); + + facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) + .assertResults("SELECT k, sum(d) AS s FROM tmp WHERE k < 0 GROUP BY k"); +} + +TEST_F(CudfDecimalTest, decimalSumIntermediateVarbinary) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 1, 2}), + makeFlatVector({12345, -2500, 10000}, DECIMAL(12, 2)), + }); + auto input2 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({2, 3}), + makeFlatVector({200, -300}, DECIMAL(12, 2)), + }); + + std::vector vectors = {input1, input2}; + createDuckDbTable(vectors); + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"sum(d) AS s"}) + .intermediateAggregation() + .finalAggregation() + .planNode(); + + facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) + .assertResults("SELECT k, sum(d) AS s FROM tmp GROUP BY k"); +} + +TEST_F(CudfDecimalTest, decimalSumGlobalPartialFinalVarbinary) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"d"}, {makeFlatVector({12345, -2500, 10000}, DECIMAL(12, 2))}); + auto input2 = makeRowVector( + {"d"}, {makeFlatVector({200, -300}, DECIMAL(12, 2))}); + + std::vector vectors = {input1, input2}; + createDuckDbTable(vectors); + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"sum(d) AS s"}) + .finalAggregation() + .planNode(); + + facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) + .assertResults("SELECT sum(d) AS s FROM tmp"); +} + +TEST_F(CudfDecimalTest, decimalSumGlobalIntermediateVarbinary) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"d"}, {makeFlatVector({12345, -2500, 10000}, DECIMAL(12, 2))}); + auto input2 = makeRowVector( + {"d"}, {makeFlatVector({200, -300}, DECIMAL(12, 2))}); + + std::vector vectors = {input1, input2}; + createDuckDbTable(vectors); + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"sum(d) AS s"}) + .intermediateAggregation() + .finalAggregation() + .planNode(); + + facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) + .assertResults("SELECT sum(d) AS s FROM tmp"); +} + +TEST_F(CudfDecimalTest, decimalSumGlobalSingle) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeFlatVector( + {12345, -2500, 10000, 200, -300}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + createDuckDbTable(vectors); + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({}, {"sum(d) AS s"}) + .planNode(); + + facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) + .assertResults("SELECT sum(d) AS s FROM tmp"); +} + +TEST_F(CudfDecimalTest, decimalSumPartialFinalVarbinaryNullGroup) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 1, 2, 2, 3, 3}), + makeNullableFlatVector( + {100, 200, std::nullopt, std::nullopt, 400, std::nullopt}, + DECIMAL(12, 2)), + }); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"sum(d) AS s"}) + .finalAggregation() + .orderBy({"k"}, false) + .planNode(); + + auto expected = makeRowVector( + {"k", "s"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {static_cast(300), + std::nullopt, + static_cast(400)}, + DECIMAL(38, 2)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalSumIntermediateVarbinaryNullGroup) { + auto rowType = ROW({ + {"k", INTEGER()}, + {"d", DECIMAL(12, 2)}, + }); + + auto input1 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {100, std::nullopt, 400}, DECIMAL(12, 2)), + }); + auto input2 = makeRowVector( + {"k", "d"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {200, std::nullopt, std::nullopt}, DECIMAL(12, 2)), + }); + + std::vector vectors = {input1, input2}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({"k"}, {"sum(d) AS s"}) + .intermediateAggregation() + .finalAggregation() + .orderBy({"k"}, false) + .planNode(); + + auto expected = makeRowVector( + {"k", "s"}, + { + makeFlatVector({1, 2, 3}), + makeNullableFlatVector( + {static_cast(300), + std::nullopt, + static_cast(400)}, + DECIMAL(38, 2)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalSumGlobalPartialFinalVarbinaryAllNulls) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeNullableFlatVector( + {std::nullopt, std::nullopt, std::nullopt}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"sum(d) AS s"}) + .finalAggregation() + .planNode(); + + auto expected = makeRowVector( + {"s"}, + {makeNullableFlatVector({std::nullopt}, DECIMAL(38, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalSumGlobalIntermediateVarbinaryAllNulls) { + auto rowType = ROW({ + {"d", DECIMAL(12, 2)}, + }); + + auto input = makeRowVector( + {"d"}, + {makeNullableFlatVector( + {std::nullopt, std::nullopt, std::nullopt}, DECIMAL(12, 2))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"sum(d) AS s"}) + .intermediateAggregation() + .finalAggregation() + .planNode(); + + auto expected = makeRowVector( + {"s"}, + {makeNullableFlatVector({std::nullopt}, DECIMAL(38, 2))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector sums = {100, -200, 300}; + std::vector counts = {1, 2, 0}; + std::vector sumValid = {true, false, true}; + std::vector countValid = {true, true, true}; + + auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto stateCol = + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + auto sumOnly = deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto stateMask = copyNullMask(stateCol->view(), stream); + auto sumMask = copyNullMask(sumOnly->view(), stream); + EXPECT_EQ(stateMask, sumMask); + + auto outSum = copyColumnData<__int128_t>(sumOnly->view(), stream); + for (size_t i = 0; i < sums.size(); ++i) { + bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; + EXPECT_EQ(isValidAt(sumMask, i), expectedValid); + if (expectedValid) { + EXPECT_EQ(outSum[i], static_cast<__int128_t>(sums[i])); + } + } +} + +TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal128) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector<__int128_t> sums = { + static_cast<__int128_t>(123450), + static_cast<__int128_t>(-25000), + static_cast<__int128_t>(100000), + }; + std::vector counts = {2, 1, 0}; + std::vector sumValid = {true, true, true}; + std::vector countValid = {true, false, true}; + + auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto stateCol = + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + auto sumOnly = deserializeDecimalSumState(stateCol->view(), 3, stream, mr); + auto stateMask = copyNullMask(stateCol->view(), stream); + auto sumMask = copyNullMask(sumOnly->view(), stream); + EXPECT_EQ(stateMask, sumMask); + + auto outSum = copyColumnData<__int128_t>(sumOnly->view(), stream); + for (size_t i = 0; i < sums.size(); ++i) { + bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; + EXPECT_EQ(isValidAt(sumMask, i), expectedValid); + if (expectedValid) { + EXPECT_EQ(outSum[i], sums[i]); + } + } +} + +TEST_F(CudfDecimalTest, decimalDeserializeSumStateAllNull) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + constexpr cudf::size_type numRows = 4; + + auto offsetsCol = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::INT32}, + numRows + 1, + cudf::mask_state::UNALLOCATED, + stream); + auto* offsetsPtr = offsetsCol->mutable_view().data(); + auto status = cudaMemsetAsync( + offsetsPtr, + 0, + static_cast(numRows + 1) * sizeof(int32_t), + stream.value()); + VELOX_CHECK_EQ(0, static_cast(status)); + stream.synchronize(); + + std::vector valid(numRows, false); + auto [nullMask, nullCount] = makeNullMask(valid, stream); + rmm::device_buffer charsBuf(0, stream); + auto stateCol = cudf::make_strings_column( + numRows, + std::move(offsetsCol), + std::move(charsBuf), + nullCount, + std::move(nullMask)); + + auto decoded = + deserializeDecimalSumStateWithCount(stateCol->view(), 2, stream, mr); + auto outSumView = decoded.sum->view(); + auto outCountView = decoded.count->view(); + + EXPECT_EQ(outSumView.size(), numRows); + EXPECT_EQ(outCountView.size(), numRows); + EXPECT_EQ(outSumView.null_count(), numRows); + EXPECT_EQ(outCountView.null_count(), numRows); + + auto outSumMask = copyNullMask(outSumView, stream); + auto outCountMask = copyNullMask(outCountView, stream); + for (size_t i = 0; i < static_cast(numRows); ++i) { + EXPECT_FALSE(isValidAt(outSumMask, i)); + EXPECT_FALSE(isValidAt(outCountMask, i)); + } +} + +TEST_F(CudfDecimalTest, decimalSerializeSumStateUsesInt64OffsetsWhenEnabled) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + ScopedEnvVar enableLargeStrings("LIBCUDF_LARGE_STRINGS_ENABLED", "1"); + ScopedEnvVar threshold("LIBCUDF_LARGE_STRINGS_THRESHOLD", "1"); + + std::vector sums = {100, -200}; + std::vector counts = {1, 1}; + + auto sumCol = makeDecimalColumn(sums, 2, nullptr, stream); + auto countCol = makeInt64Column(counts, nullptr, stream); + auto stateCol = + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + + cudf::strings_column_view strings(stateCol->view()); + EXPECT_EQ(strings.offsets().type().id(), cudf::type_id::INT64); +} + +TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + ScopedEnvVar enableLargeStrings("LIBCUDF_LARGE_STRINGS_ENABLED", "1"); + ScopedEnvVar threshold("LIBCUDF_LARGE_STRINGS_THRESHOLD", "1"); + + std::vector sums = {100, -200, 300, 400}; + std::vector counts = {1, 0, 2, 3}; + std::vector sumValid = {true, true, false, true}; + std::vector countValid = {true, true, true, false}; + + auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto stateCol = + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + + cudf::strings_column_view strings(stateCol->view()); + EXPECT_EQ(strings.offsets().type().id(), cudf::type_id::INT64); + + auto decoded = + deserializeDecimalSumStateWithCount(stateCol->view(), 2, stream, mr); + auto outSumView = decoded.sum->view(); + auto outCountView = decoded.count->view(); + auto outSum = copyColumnData<__int128_t>(outSumView, stream); + auto outCount = copyColumnData(outCountView, stream); + auto outSumMask = copyNullMask(outSumView, stream); + auto outCountMask = copyNullMask(outCountView, stream); + + EXPECT_EQ(outSumView.size(), sums.size()); + EXPECT_EQ(outCountView.size(), counts.size()); + EXPECT_EQ(outSumMask, outCountMask); + + for (size_t i = 0; i < sums.size(); ++i) { + bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; + EXPECT_EQ(isValidAt(outSumMask, i), expectedValid); + EXPECT_EQ(isValidAt(outCountMask, i), expectedValid); + if (expectedValid) { + EXPECT_EQ(outSum[i], static_cast<__int128_t>(sums[i])); + EXPECT_EQ(outCount[i], counts[i]); + } + } +} + +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector sums = {100, 105, 250, -125}; + std::vector counts = {4, 2, 0, 2}; + std::vector sumValid = {true, true, true, true}; + std::vector countValid = {true, false, true, true}; + + auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + + auto avgMask = copyNullMask(avgCol->view(), stream); + auto outAvg = copyColumnData(avgCol->view(), stream); + + auto avgUnscaled = [](int128_t sum, int64_t count) { + __int128_t out = 0; + facebook::velox::DecimalUtil:: + divideWithRoundUp<__int128_t, __int128_t, int64_t>( + out, sum, count, false, 0, 0); + return static_cast(out); + }; + + for (size_t i = 0; i < sums.size(); ++i) { + bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; + EXPECT_EQ(isValidAt(avgMask, i), expectedValid); + if (expectedValid) { + EXPECT_EQ(outAvg[i], avgUnscaled(sums[i], counts[i])); + } + } +} + +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector<__int128_t> sums = { + static_cast<__int128_t>(123450), + static_cast<__int128_t>(-25000), + static_cast<__int128_t>(100000), + }; + std::vector counts = {3, 2, 0}; + std::vector sumValid = {true, true, true}; + std::vector countValid = {true, true, true}; + + auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + + auto avgMask = copyNullMask(avgCol->view(), stream); + auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); + + auto avgUnscaled = [](int128_t sum, int64_t count) { + __int128_t out = 0; + facebook::velox::DecimalUtil:: + divideWithRoundUp<__int128_t, __int128_t, int64_t>( + out, sum, count, false, 0, 0); + return out; + }; + + for (size_t i = 0; i < sums.size(); ++i) { + bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; + EXPECT_EQ(isValidAt(avgMask, i), expectedValid); + if (expectedValid) { + EXPECT_EQ(outAvg[i], avgUnscaled(sums[i], counts[i])); + } + } +} + +TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector sums = {100, -200, 300, 400}; + std::vector counts = {1, 0, 2, 3}; + std::vector sumValid = {true, true, false, true}; + std::vector countValid = {true, true, true, false}; + + auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto stateCol = + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + auto stateMask = copyNullMask(stateCol->view(), stream); + + auto decoded = + deserializeDecimalSumStateWithCount(stateCol->view(), 2, stream, mr); + auto outSumView = decoded.sum->view(); + auto outCountView = decoded.count->view(); + auto outSum = copyColumnData<__int128_t>(outSumView, stream); + auto outCount = copyColumnData(outCountView, stream); + auto outSumMask = copyNullMask(outSumView, stream); + auto outCountMask = copyNullMask(outCountView, stream); + + EXPECT_EQ(stateMask, outSumMask); + EXPECT_EQ(stateMask, outCountMask); + + for (size_t i = 0; i < sums.size(); ++i) { + bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; + EXPECT_EQ(isValidAt(stateMask, i), expectedValid); + if (expectedValid) { + EXPECT_EQ(outSum[i], static_cast<__int128_t>(sums[i])); + EXPECT_EQ(outCount[i], counts[i]); + } + } +} + +TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal128) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector<__int128_t> sums = { + static_cast<__int128_t>(123450), + static_cast<__int128_t>(-25000), + static_cast<__int128_t>(100000), + }; + std::vector counts = {2, 1, 0}; + std::vector sumValid = {true, false, true}; + std::vector countValid = {true, true, true}; + + auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto stateCol = + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + auto stateMask = copyNullMask(stateCol->view(), stream); + + auto decoded = + deserializeDecimalSumStateWithCount(stateCol->view(), 3, stream, mr); + auto outSumView = decoded.sum->view(); + auto outCountView = decoded.count->view(); + auto outSum = copyColumnData<__int128_t>(outSumView, stream); + auto outCount = copyColumnData(outCountView, stream); + auto outSumMask = copyNullMask(outSumView, stream); + auto outCountMask = copyNullMask(outCountView, stream); + + EXPECT_EQ(stateMask, outSumMask); + EXPECT_EQ(stateMask, outCountMask); + + for (size_t i = 0; i < sums.size(); ++i) { + bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; + EXPECT_EQ(isValidAt(stateMask, i), expectedValid); + if (expectedValid) { + EXPECT_EQ(outSum[i], sums[i]); + EXPECT_EQ(outCount[i], counts[i]); + } + } +} + +TEST_F(CudfDecimalTest, cudfVarbinaryArrowRoundTrip) { + auto input = makeRowVector( + {"bin"}, + {makeNullableFlatVector( + {std::string("abc"), std::nullopt, std::string("xyz")}, + VARBINARY())}); + + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + auto cudfTable = with_arrow::toCudfTable(input, pool(), stream, mr); + auto roundTrip = + with_arrow::toVeloxColumn(cudfTable->view(), pool(), "rt_", stream, mr); + + ASSERT_EQ(roundTrip->childAt(0)->type()->kind(), TypeKind::VARCHAR); + VELOX_ASSERT_THROW( + roundTrip->setType(ROW({{"rt_0", VARBINARY()}})), + "Cannot change vector type"); + + auto expected = makeRowVector( + {"rt_0"}, + {makeNullableFlatVector( + {std::string("abc"), std::nullopt, std::string("xyz")}, VARCHAR())}); + + facebook::velox::test::assertEqualVectors(expected, roundTrip); +} + +TEST_F(CudfDecimalTest, cudfVarbinaryArrowRoundTripWithExpectedType) { + auto input = makeRowVector( + {"bin"}, + {makeNullableFlatVector( + {std::string("abc"), std::nullopt, std::string("xyz")}, + VARBINARY())}); + + auto expectedType = ROW({{"bin", VARBINARY()}}); + + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + auto cudfTable = with_arrow::toCudfTable(input, pool(), stream, mr); + auto roundTrip = with_arrow::toVeloxColumn( + cudfTable->view(), pool(), expectedType, "rt_", stream, mr); + + ASSERT_EQ(roundTrip->childAt(0)->type()->kind(), TypeKind::VARBINARY); + + auto expected = makeRowVector( + {"rt_0"}, + {makeNullableFlatVector( + {std::string("abc"), std::nullopt, std::string("xyz")}, + VARBINARY())}); + + facebook::velox::test::assertEqualVectors(expected, roundTrip); +} + +TEST_F(CudfDecimalTest, cudfVarbinaryRowTypeMismatch) { + auto input = makeRowVector( + {"l_returnflag", + "l_linestatus", + "avg_51", + "avg_52", + "avg_53", + "count_54", + "sum_47", + "sum_48", + "sum_49", + "sum_50"}, + {makeFlatVector({"A", "B"}, VARCHAR()), + makeFlatVector({"F", "O"}, VARCHAR()), + makeFlatVector({"x", "y"}, VARBINARY()), + makeFlatVector({"p", "q"}, VARBINARY()), + makeFlatVector({"m", "n"}, VARBINARY()), + makeFlatVector({10, 20}, BIGINT()), + makeFlatVector({"u", "v"}, VARBINARY()), + makeFlatVector({"r", "s"}, VARBINARY()), + makeFlatVector({"t", "w"}, VARBINARY()), + makeFlatVector({"c", "d"}, VARBINARY())}); + + auto expectedType = ROW({ + {"l_returnflag", VARCHAR()}, + {"l_linestatus", VARCHAR()}, + {"avg_51", VARBINARY()}, + {"avg_52", VARBINARY()}, + {"avg_53", VARBINARY()}, + {"count_54", BIGINT()}, + {"sum_47", VARBINARY()}, + {"sum_48", VARBINARY()}, + {"sum_49", VARBINARY()}, + {"sum_50", VARBINARY()}, + }); + + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + auto cudfTable = with_arrow::toCudfTable(input, pool(), stream, mr); + auto roundTrip = + with_arrow::toVeloxColumn(cudfTable->view(), pool(), "rt_", stream, mr); + + ASSERT_EQ(roundTrip->childAt(2)->type()->kind(), TypeKind::VARCHAR); + ASSERT_EQ(roundTrip->childAt(3)->type()->kind(), TypeKind::VARCHAR); + ASSERT_EQ(roundTrip->childAt(4)->type()->kind(), TypeKind::VARCHAR); + ASSERT_EQ(roundTrip->childAt(6)->type()->kind(), TypeKind::VARCHAR); + ASSERT_EQ(roundTrip->childAt(7)->type()->kind(), TypeKind::VARCHAR); + ASSERT_EQ(roundTrip->childAt(8)->type()->kind(), TypeKind::VARCHAR); + ASSERT_EQ(roundTrip->childAt(9)->type()->kind(), TypeKind::VARCHAR); + + VELOX_ASSERT_THROW( + roundTrip->setType(expectedType), "Cannot change vector type"); +} + +} // namespace +} // namespace facebook::velox::cudf_velox From 256db018a94b039bd0a4254d708445c6a8a23227 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 3 Apr 2026 13:54:08 -0700 Subject: [PATCH 02/64] Fix after rebase, and format --- .../cudf/exec/CudfHashAggregation.cpp | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfHashAggregation.cpp b/velox/experimental/cudf/exec/CudfHashAggregation.cpp index 8fcc59c532e..d3f14b8439d 100644 --- a/velox/experimental/cudf/exec/CudfHashAggregation.cpp +++ b/velox/experimental/cudf/exec/CudfHashAggregation.cpp @@ -54,67 +54,67 @@ using namespace facebook::velox; using facebook::velox::cudf_velox::get_output_mr; using facebook::velox::cudf_velox::get_temp_mr; -#define DEFINE_SIMPLE_AGGREGATOR(Name, name, KIND) \ - struct Name##Aggregator : cudf_velox::CudfHashAggregation::Aggregator { \ - Name##Aggregator( \ - core::AggregationNode::Step step, \ - uint32_t inputIndex, \ - VectorPtr constant, \ - bool is_global, \ - const TypePtr& resultType) \ - : Aggregator( \ - step, \ - cudf::aggregation::KIND, \ - inputIndex, \ - constant, \ - is_global, \ - resultType) {} \ - \ - void addGroupbyRequest( \ - cudf::table_view const& tbl, \ - std::vector& requests, \ - rmm::cuda_stream_view stream) override { \ - VELOX_CHECK( \ - constant == nullptr, \ - #Name "Aggregator does not yet support constant input"); \ - auto& request = requests.emplace_back(); \ - output_idx = requests.size() - 1; \ - request.values = tbl.column(inputIndex); \ - request.aggregations.push_back( \ - cudf::make_##name##_aggregation()); \ - } \ - \ - std::unique_ptr makeOutputColumn( \ - std::vector& results, \ - rmm::cuda_stream_view stream) override { \ - auto col = std::move(results[output_idx].results[0]); \ - auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); \ - if (col->type() != cudfResType) { \ - col = cudf::cast(*col, cudfResType, stream, get_output_mr()); \ - } \ - return col; \ - } \ - \ - std::unique_ptr doReduce( \ - cudf::table_view const& input, \ - TypePtr const& outputType, \ - rmm::cuda_stream_view stream, \ - vector_size_t /*inputRowCount*/) override { \ - auto const aggRequest = \ - cudf::make_##name##_aggregation(); \ - auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); \ - auto const resultScalar = cudf::reduce( \ - input.column(inputIndex), \ - *aggRequest, \ - cudfOutType, \ - stream, \ - get_temp_mr()); \ - return cudf::make_column_from_scalar( \ - *resultScalar, 1, stream, get_output_mr()); \ - } \ - \ - private: \ - uint32_t output_idx; \ +#define DEFINE_SIMPLE_AGGREGATOR(Name, name, KIND) \ + struct Name##Aggregator : cudf_velox::CudfHashAggregation::Aggregator { \ + Name##Aggregator( \ + core::AggregationNode::Step step, \ + uint32_t inputIndex, \ + VectorPtr constant, \ + bool is_global, \ + const TypePtr& resultType) \ + : Aggregator( \ + step, \ + cudf::aggregation::KIND, \ + inputIndex, \ + constant, \ + is_global, \ + resultType) {} \ + \ + void addGroupbyRequest( \ + cudf::table_view const& tbl, \ + std::vector& requests, \ + rmm::cuda_stream_view stream) override { \ + VELOX_CHECK( \ + constant == nullptr, \ + #Name "Aggregator does not yet support constant input"); \ + auto& request = requests.emplace_back(); \ + output_idx = requests.size() - 1; \ + request.values = tbl.column(inputIndex); \ + request.aggregations.push_back( \ + cudf::make_##name##_aggregation()); \ + } \ + \ + std::unique_ptr makeOutputColumn( \ + std::vector& results, \ + rmm::cuda_stream_view stream) override { \ + auto col = std::move(results[output_idx].results[0]); \ + auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); \ + if (col->type() != cudfResType) { \ + col = cudf::cast(*col, cudfResType, stream, get_output_mr()); \ + } \ + return col; \ + } \ + \ + std::unique_ptr doReduce( \ + cudf::table_view const& input, \ + TypePtr const& outputType, \ + rmm::cuda_stream_view stream, \ + vector_size_t /*inputRowCount*/) override { \ + auto const aggRequest = \ + cudf::make_##name##_aggregation(); \ + auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); \ + auto const resultScalar = cudf::reduce( \ + input.column(inputIndex), \ + *aggRequest, \ + cudfOutType, \ + stream, \ + get_temp_mr()); \ + return cudf::make_column_from_scalar( \ + *resultScalar, 1, stream, get_output_mr()); \ + } \ + \ + private: \ + uint32_t output_idx; \ }; DEFINE_SIMPLE_AGGREGATOR(Sum, sum, SUM) @@ -259,7 +259,8 @@ struct DecimalSumOrAvgAggregator : cudf_velox::CudfHashAggregation::Aggregator { std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, - rmm::cuda_stream_view stream) override { + rmm::cuda_stream_view stream, + vector_size_t /*inputRowCount*/) override { if (step == core::AggregationNode::Step::kSingle && isAvg_) { auto const sumAgg = cudf::make_sum_aggregation(); @@ -1223,7 +1224,6 @@ auto toAggregators( aggregators.push_back(createAggregator( companionStep, aggregate, - kind, inputIndex, constant, isGlobal, From b3e3426bfc93e929566afdd82d0fa608c68d2a0d Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 7 Apr 2026 17:53:24 -0700 Subject: [PATCH 03/64] Avoid clang-tidy warnings about structs with virtual functions but non-virtual destructor --- velox/experimental/cudf/exec/CudfHashAggregation.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/velox/experimental/cudf/exec/CudfHashAggregation.h b/velox/experimental/cudf/exec/CudfHashAggregation.h index 4081bd4a5bc..51b7a72939b 100644 --- a/velox/experimental/cudf/exec/CudfHashAggregation.h +++ b/velox/experimental/cudf/exec/CudfHashAggregation.h @@ -55,6 +55,8 @@ class CudfHashAggregation : public CudfOperatorBase { std::vector& results, rmm::cuda_stream_view stream) = 0; + virtual ~Aggregator() = default; + protected: Aggregator( core::AggregationNode::Step step, From f98a3d18501ca12e3637a6728fa4271e76d7b9dc Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 24 Apr 2026 13:17:57 -0700 Subject: [PATCH 04/64] Reinstate agg registration changes --- .../cudf/exec/AggregationRegistry.cpp | 115 +++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/velox/experimental/cudf/exec/AggregationRegistry.cpp b/velox/experimental/cudf/exec/AggregationRegistry.cpp index e0fe6c28606..dccc50811c0 100644 --- a/velox/experimental/cudf/exec/AggregationRegistry.cpp +++ b/velox/experimental/cudf/exec/AggregationRegistry.cpp @@ -82,6 +82,38 @@ void registerCommonAggregationFunctions( .argumentType("double") .build()}; + // Decimal sum signatures. + auto decimalSumSingle = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("decimal(38, a_scale)") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalSumPartial = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("varbinary") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalSumFinal = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_scale") + .returnType("decimal(38, a_scale)") + .argumentType("varbinary") + .build()}; + auto decimalSumIntermediate = + std::vector{FunctionSignatureBuilder() + .returnType("varbinary") + .argumentType("varbinary") + .build()}; + + sumSingleSignatures.insert( + sumSingleSignatures.end(), + decimalSumSingle.begin(), + decimalSumSingle.end()); + registerAggregationFunctionForStep( registry, prefix + "sum", @@ -109,6 +141,12 @@ void registerCommonAggregationFunctions( .returnType("double") .argumentType("double") .build()}; + + sumPartialSignatures.insert( + sumPartialSignatures.end(), + decimalSumPartial.begin(), + decimalSumPartial.end()); + registerAggregationFunctionForStep( registry, prefix + "sum", @@ -125,16 +163,27 @@ void registerCommonAggregationFunctions( .argumentType("double") .build()}; + auto sumFinalSignatures = sumFinalIntermediateSignatures; + sumFinalSignatures.insert( + sumFinalSignatures.end(), decimalSumFinal.begin(), decimalSumFinal.end()); + registerAggregationFunctionForStep( registry, prefix + "sum", core::AggregationNode::Step::kFinal, - sumFinalIntermediateSignatures); + sumFinalSignatures); + + auto sumIntermediateSignatures = sumFinalIntermediateSignatures; + sumIntermediateSignatures.insert( + sumIntermediateSignatures.end(), + decimalSumIntermediate.begin(), + decimalSumIntermediate.end()); + registerAggregationFunctionForStep( registry, prefix + "sum", core::AggregationNode::Step::kIntermediate, - sumFinalIntermediateSignatures); + sumIntermediateSignatures); auto countSinglePartialSignatures = std::vector{ FunctionSignatureBuilder() @@ -226,6 +275,12 @@ void registerCommonAggregationFunctions( FunctionSignatureBuilder() .returnType("varchar") .argumentType("varchar") + .build(), + FunctionSignatureBuilder() + .integerVariable("p") + .integerVariable("s") + .returnType("decimal(p,s)") + .argumentType("decimal(p,s)") .build()}; registerAggregationFunctionForStep( @@ -288,6 +343,39 @@ void registerCommonAggregationFunctions( .argumentType("double") .build()}; + // Decimal avg signatures. + auto decimalAvgSingle = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("decimal(a_precision, a_scale)") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalAvgPartial = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("varbinary") + .argumentType("decimal(a_precision, a_scale)") + .build()}; + auto decimalAvgFinal = std::vector{ + FunctionSignatureBuilder() + .integerVariable("a_precision") + .integerVariable("a_scale") + .returnType("decimal(a_precision, a_scale)") + .argumentType("varbinary") + .build()}; + auto decimalAvgIntermediate = + std::vector{FunctionSignatureBuilder() + .returnType("varbinary") + .argumentType("varbinary") + .build()}; + + avgSingleSignatures.insert( + avgSingleSignatures.end(), + decimalAvgSingle.begin(), + decimalAvgSingle.end()); + registerAggregationFunctionForStep( registry, prefix + "avg", @@ -315,17 +403,28 @@ void registerCommonAggregationFunctions( .returnType("row(double,bigint)") .argumentType("double") .build()}; + + avgPartialSignatures.insert( + avgPartialSignatures.end(), + decimalAvgPartial.begin(), + decimalAvgPartial.end()); + registerAggregationFunctionForStep( registry, prefix + "avg", core::AggregationNode::Step::kPartial, avgPartialSignatures); - auto avgFinalSignatures = std::vector{ + auto avgFinalIntermediateSignatures = std::vector{ FunctionSignatureBuilder() .returnType("double") .argumentType("row(double,bigint)") .build()}; + + auto avgFinalSignatures = avgFinalIntermediateSignatures; + avgFinalSignatures.insert( + avgFinalSignatures.end(), decimalAvgFinal.begin(), decimalAvgFinal.end()); + registerAggregationFunctionForStep( registry, prefix + "avg", @@ -337,6 +436,16 @@ void registerCommonAggregationFunctions( .returnType("row(double,bigint)") .argumentType("row(double,bigint)") .build()}; + + // WHY DOES SUM NOT HAVE THE EQUIVALENT OF THE ABOVE? + // THE ABOVE THEN CLASHES WITH BELOW + // @mattgara HELP! :) + // auto avgIntermediateSignatures = avgFinalIntermediateSignatures; + avgIntermediateSignatures.insert( + avgIntermediateSignatures.end(), + decimalAvgIntermediate.begin(), + decimalAvgIntermediate.end()); + registerAggregationFunctionForStep( registry, prefix + "avg", From 1e257e8f7d2ad7fd4c3dbdec7d52d3f4da1e2a57 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 24 Apr 2026 14:28:09 -0700 Subject: [PATCH 05/64] AI-assisted doReduce changes --- velox/experimental/cudf/exec/CudfGroupby.cpp | 147 +--------------- velox/experimental/cudf/exec/CudfReduce.cpp | 173 +++++++++++++++++++ 2 files changed, 177 insertions(+), 143 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 601104c7e80..a4a0298fea2 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -87,8 +87,8 @@ DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Sum, sum, SUM) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Min, min, MIN) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Max, max, MAX) -struct DecimalSumOrAvgAggregator : GroupbyAggregator { - DecimalSumOrAvgAggregator( +struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { + GroupbyDecimalSumOrAvgAggregator( core::AggregationNode::Step step, uint32_t inputIndex, VectorPtr constant, @@ -215,145 +215,6 @@ struct DecimalSumOrAvgAggregator : GroupbyAggregator { return col; } - std::unique_ptr doReduce( - cudf::table_view const& input, - TypePtr const& outputType, - rmm::cuda_stream_view stream, - vector_size_t /*inputRowCount*/) override { - if (step == core::AggregationNode::Step::kSingle && isAvg_) { - auto const sumAgg = - cudf::make_sum_aggregation(); - cudf::column_view inputCol = input.column(inputIndex); - auto sumScalar = cudf::reduce( - inputCol, - *sumAgg, - inputCol.type(), - stream, - cudf_velox::get_temp_mr()); - auto countAgg = cudf::make_count_aggregation( - cudf::null_policy::EXCLUDE); - auto countScalar = cudf::reduce( - inputCol, - *countAgg, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, cudf_velox::get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, cudf_velox::get_output_mr()); - return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); - } - auto const aggRequest = - cudf::make_sum_aggregation(); - cudf::column_view inputCol = input.column(inputIndex); - if (step == core::AggregationNode::Step::kPartial) { - auto sumScalar = cudf::reduce( - inputCol, - *aggRequest, - inputCol.type(), - stream, - cudf_velox::get_temp_mr()); - auto countAgg = cudf::make_count_aggregation( - cudf::null_policy::EXCLUDE); - auto countScalar = cudf::reduce( - inputCol, - *countAgg, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, cudf_velox::get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, cudf_velox::get_output_mr()); - return cudf_velox::serializeDecimalSumState( - sumCol->view(), - countCol->view(), - stream, - cudf_velox::get_output_mr()); - } - if (step == core::AggregationNode::Step::kIntermediate && - inputCol.type().id() == cudf::type_id::STRING) { - auto scale = outputType->isDecimal() - ? getDecimalPrecisionScale(*outputType).second - : 0; - auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( - inputCol, scale, stream, cudf_velox::get_output_mr()); - auto sumScalar = cudf::reduce( - decoded.sum->view(), - *aggRequest, - decoded.sum->view().type(), - stream, - cudf_velox::get_temp_mr()); - auto countScalar = cudf::reduce( - decoded.count->view(), - *aggRequest, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, cudf_velox::get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, cudf_velox::get_output_mr()); - return cudf_velox::serializeDecimalSumState( - sumCol->view(), - countCol->view(), - stream, - cudf_velox::get_output_mr()); - } - if (step == core::AggregationNode::Step::kFinal && - inputCol.type().id() == cudf::type_id::STRING) { - auto scale = getDecimalPrecisionScale(*outputType).second; - if (isAvg_) { - // AVG - // deserialize the results (sum and count) - auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( - inputCol, scale, stream, cudf_velox::get_output_mr()); - // reduce the two results to get final sum and count scalars - auto sumScalar = cudf::reduce( - sumAndCount.sum->view(), - *aggRequest, - sumAndCount.sum->view().type(), - stream, - cudf_velox::get_temp_mr()); - auto countScalar = cudf::reduce( - sumAndCount.count->view(), - *aggRequest, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_temp_mr()); - // convert to columns in order to perform division, as we cannot divide - // scalars directly - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, cudf_velox::get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, cudf_velox::get_output_mr()); - return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); - } else { - // SUM - decodedSum_ = cudf_velox::deserializeDecimalSumState( - inputCol, scale, stream, cudf_velox::get_output_mr()); - inputCol = decodedSum_->view(); - // @TODO does this need to drop through to the code below - // or can we just do that stuff here, and not need decodedSum_ or - // decodedCount_ why we do have those anyway if they're only set in - // addGroupbyRequest() and either overwritten or not even used here? - // what does the final cudf::reduce() below actually do? - } - } - auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); - std::unique_ptr castedInput; - if (outputType->isDecimal() && inputCol.type() != cudfOutType) { - castedInput = cudf::cast( - inputCol, cudfOutType, stream, cudf_velox::get_output_mr()); - inputCol = castedInput->view(); - } - auto const resultScalar = cudf::reduce( - inputCol, *aggRequest, cudfOutType, stream, cudf_velox::get_temp_mr()); - return cudf::make_column_from_scalar( - *resultScalar, 1, stream, cudf_velox::get_output_mr()); - } - private: std::unique_ptr computeAvgColumn( std::unique_ptr sum, @@ -597,7 +458,7 @@ std::unique_ptr createGroupbyAggregator( auto prefix = cudf_velox::CudfConfig::getInstance().functionNamePrefix; if (kind.rfind(prefix + "sum", 0) == 0) { if (p.isDecimalInput) { - return std::make_unique( + return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType, false); } return std::make_unique( @@ -614,7 +475,7 @@ std::unique_ptr createGroupbyAggregator( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "avg", 0) == 0) { if (p.isDecimalInput) { - return std::make_unique( + return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType, true); } return std::make_unique( diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index f2176002e69..6e4e7469677 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -19,6 +19,7 @@ #include "velox/experimental/cudf/exec/CudfAggregation.h" #include "velox/experimental/cudf/exec/CudfFilterProject.h" #include "velox/experimental/cudf/exec/CudfReduce.h" +#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/Utilities.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" @@ -27,6 +28,7 @@ #include "velox/exec/AggregateFunctionRegistry.h" #include "velox/exec/Task.h" #include "velox/expression/Expr.h" +#include "velox/type/Type.h" #include #include @@ -251,6 +253,169 @@ struct ReduceMeanAggregator : ReduceAggregator { } }; +struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { + ReduceDecimalSumOrAvgAggregator( + core::AggregationNode::Step step, + uint32_t inputIndex, + VectorPtr constant, + const TypePtr& resultType, + bool isAvg) + : ReduceAggregator(step, inputIndex, constant, resultType), + isAvg_(isAvg) {} + + std::unique_ptr doReduce( + cudf::table_view const& input, + TypePtr const& outputType, + rmm::cuda_stream_view stream, + vector_size_t /*inputRowCount*/) override { + if (step == core::AggregationNode::Step::kSingle && isAvg_) { + auto const sumAgg = + cudf::make_sum_aggregation(); + cudf::column_view inputCol = input.column(inputIndex); + auto sumScalar = cudf::reduce( + inputCol, + *sumAgg, + inputCol.type(), + stream, + get_temp_mr()); + auto countAgg = cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE); + auto countScalar = cudf::reduce( + inputCol, + *countAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, get_output_mr()); + return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); + } + std::unique_ptr stringDecodedSum; + auto const aggRequest = + cudf::make_sum_aggregation(); + cudf::column_view inputCol = input.column(inputIndex); + if (step == core::AggregationNode::Step::kPartial) { + auto sumScalar = cudf::reduce( + inputCol, + *aggRequest, + inputCol.type(), + stream, + get_temp_mr()); + auto countAgg = cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE); + auto countScalar = cudf::reduce( + inputCol, + *countAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, get_output_mr()); + return cudf_velox::serializeDecimalSumState( + sumCol->view(), + countCol->view(), + stream, + get_output_mr()); + } + if (step == core::AggregationNode::Step::kIntermediate && + inputCol.type().id() == cudf::type_id::STRING) { + auto scale = outputType->isDecimal() + ? getDecimalPrecisionScale(*outputType).second + : 0; + auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + inputCol, scale, stream, get_output_mr()); + auto sumScalar = cudf::reduce( + decoded.sum->view(), + *aggRequest, + decoded.sum->view().type(), + stream, + get_temp_mr()); + auto countScalar = cudf::reduce( + decoded.count->view(), + *aggRequest, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, get_output_mr()); + return cudf_velox::serializeDecimalSumState( + sumCol->view(), + countCol->view(), + stream, + get_output_mr()); + } + if (step == core::AggregationNode::Step::kFinal && + inputCol.type().id() == cudf::type_id::STRING) { + auto scale = getDecimalPrecisionScale(*outputType).second; + if (isAvg_) { + auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( + inputCol, scale, stream, get_output_mr()); + auto sumScalar = cudf::reduce( + sumAndCount.sum->view(), + *aggRequest, + sumAndCount.sum->view().type(), + stream, + get_temp_mr()); + auto countScalar = cudf::reduce( + sumAndCount.count->view(), + *aggRequest, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = cudf::make_column_from_scalar( + *sumScalar, 1, stream, get_output_mr()); + auto countCol = cudf::make_column_from_scalar( + *countScalar, 1, stream, get_output_mr()); + return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); + } + stringDecodedSum = cudf_velox::deserializeDecimalSumState( + inputCol, scale, stream, get_output_mr()); + inputCol = stringDecodedSum->view(); + } + auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); + std::unique_ptr castedInput; + if (outputType->isDecimal() && inputCol.type() != cudfOutType) { + castedInput = cudf::cast( + inputCol, cudfOutType, stream, get_output_mr()); + inputCol = castedInput->view(); + } + auto const resultScalar = cudf::reduce( + inputCol, *aggRequest, cudfOutType, stream, get_temp_mr()); + return cudf::make_column_from_scalar( + *resultScalar, 1, stream, get_output_mr()); + } + + private: + std::unique_ptr computeAvgColumn( + std::unique_ptr sum, + std::unique_ptr count, + rmm::cuda_stream_view stream) const { + if (count->type().id() != cudf::type_id::INT64) { + count = cudf::cast( + *count, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_output_mr()); + } + auto avgCol = cudf_velox::computeDecimalAverage( + sum->view(), count->view(), stream, get_output_mr()); + auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); + if (avgCol->type() != cudfOutType) { + avgCol = cudf::cast( + avgCol->view(), cudfOutType, stream, get_output_mr()); + } + return avgCol; + } + + const bool isAvg_{false}; +}; + struct ApproxDistinctAggregator : ReduceAggregator { static constexpr cudf::null_policy kNullPolicy = cudf::null_policy::EXCLUDE; static constexpr cudf::nan_policy kNanPolicy = cudf::nan_policy::NAN_IS_VALID; @@ -451,6 +616,10 @@ std::unique_ptr createReduceAggregator( auto const& kind = p.kind; auto prefix = cudf_velox::CudfConfig::getInstance().functionNamePrefix; if (kind.rfind(prefix + "sum", 0) == 0) { + if (p.isDecimalInput) { + return std::make_unique( + p.companionStep, p.inputIndex, p.constant, p.resultType, false); + } return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "count", 0) == 0) { @@ -464,6 +633,10 @@ std::unique_ptr createReduceAggregator( return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "avg", 0) == 0) { + if (p.isDecimalInput) { + return std::make_unique( + p.companionStep, p.inputIndex, p.constant, p.resultType, true); + } return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "approx_distinct", 0) == 0) { From 6ebe3d6ed2bc4432fe597abf591c1458da5df31c Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 24 Apr 2026 14:34:32 -0700 Subject: [PATCH 06/64] Format --- velox/experimental/cudf/exec/CudfGroupby.cpp | 87 ++++++++------- velox/experimental/cudf/exec/CudfReduce.cpp | 109 ++++++++----------- 2 files changed, 89 insertions(+), 107 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index a4a0298fea2..2cc654bca70 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -45,42 +45,42 @@ using cudf_velox::get_temp_mr; using cudf_velox::GroupbyAggregator; using cudf_velox::ResolvedAggregateInfo; -#define DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Name, name, KIND) \ - struct Groupby##Name##Aggregator : GroupbyAggregator { \ - Groupby##Name##Aggregator( \ - core::AggregationNode::Step step, \ - uint32_t inputIndex, \ - VectorPtr constant, \ - const TypePtr& resultType) \ - : GroupbyAggregator(step, inputIndex, constant, resultType) {} \ - \ - void addGroupbyRequest( \ - cudf::table_view const& tbl, \ - std::vector& requests, \ - rmm::cuda_stream_view stream) override { \ - VELOX_CHECK( \ - constant == nullptr, \ - #Name "Aggregator does not yet support constant input"); \ - auto& request = requests.emplace_back(); \ - output_idx = requests.size() - 1; \ - request.values = tbl.column(inputIndex); \ - request.aggregations.push_back( \ - cudf::make_##name##_aggregation()); \ - } \ - \ - std::unique_ptr makeOutputColumn( \ - std::vector& results, \ - rmm::cuda_stream_view stream) override { \ - auto col = std::move(results[output_idx].results[0]); \ - const auto cudfType = cudf_velox::veloxToCudfDataType(resultType); \ - if (col->type() != cudfType) { \ - col = cudf::cast(*col, cudfType, stream, get_output_mr()); \ - } \ - return col; \ - } \ - \ - private: \ - uint32_t output_idx; \ +#define DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Name, name, KIND) \ + struct Groupby##Name##Aggregator : GroupbyAggregator { \ + Groupby##Name##Aggregator( \ + core::AggregationNode::Step step, \ + uint32_t inputIndex, \ + VectorPtr constant, \ + const TypePtr& resultType) \ + : GroupbyAggregator(step, inputIndex, constant, resultType) {} \ + \ + void addGroupbyRequest( \ + cudf::table_view const& tbl, \ + std::vector& requests, \ + rmm::cuda_stream_view stream) override { \ + VELOX_CHECK( \ + constant == nullptr, \ + #Name "Aggregator does not yet support constant input"); \ + auto& request = requests.emplace_back(); \ + output_idx = requests.size() - 1; \ + request.values = tbl.column(inputIndex); \ + request.aggregations.push_back( \ + cudf::make_##name##_aggregation()); \ + } \ + \ + std::unique_ptr makeOutputColumn( \ + std::vector& results, \ + rmm::cuda_stream_view stream) override { \ + auto col = std::move(results[output_idx].results[0]); \ + const auto cudfType = cudf_velox::veloxToCudfDataType(resultType); \ + if (col->type() != cudfType) { \ + col = cudf::cast(*col, cudfType, stream, get_output_mr()); \ + } \ + return col; \ + } \ + \ + private: \ + uint32_t output_idx; \ }; DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Sum, sum, SUM) @@ -287,8 +287,7 @@ struct GroupbyCountAggregator : GroupbyAggregator { zero, col->size(), stream, get_output_mr()); } // cudf produces int32 for count but velox expects int64. - const auto cudfOutputType = - cudf_velox::veloxToCudfDataType(resultType); + const auto cudfOutputType = cudf_velox::veloxToCudfDataType(resultType); if (col->type() != cudfOutputType) { col = cudf::cast(*col, cudfOutputType, stream, get_output_mr()); } @@ -368,8 +367,10 @@ struct GroupbyMeanAggregator : GroupbyAggregator { auto count = std::move(results[sumIdx_].results[1]); auto const size = sum->size(); - auto const cudfSumType = cudf_velox::veloxToCudfDataType(outputType->childAt(0)); - auto const cudfCountType = cudf_velox::veloxToCudfDataType(outputType->childAt(1)); + auto const cudfSumType = + cudf_velox::veloxToCudfDataType(outputType->childAt(0)); + auto const cudfCountType = + cudf_velox::veloxToCudfDataType(outputType->childAt(1)); if (sum->type() != cudf::data_type(cudfSumType)) { sum = cudf::cast( *sum, cudf::data_type(cudfSumType), stream, get_output_mr()); @@ -404,8 +405,10 @@ struct GroupbyMeanAggregator : GroupbyAggregator { auto count = std::move(results[countIdx_].results[0]); auto size = sum->size(); - auto const cudfSumType = cudf_velox::veloxToCudfDataType(outputType->childAt(0)); - auto const cudfCountType = cudf_velox::veloxToCudfDataType(outputType->childAt(1)); + auto const cudfSumType = + cudf_velox::veloxToCudfDataType(outputType->childAt(0)); + auto const cudfCountType = + cudf_velox::veloxToCudfDataType(outputType->childAt(1)); if (sum->type() != cudf::data_type(cudfSumType)) { sum = cudf::cast( *sum, cudf::data_type(cudfSumType), stream, get_output_mr()); diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 6e4e7469677..80c768e609d 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -47,33 +47,32 @@ using facebook::velox::cudf_velox::get_temp_mr; using facebook::velox::cudf_velox::ReduceAggregator; using facebook::velox::cudf_velox::ResolvedAggregateInfo; -#define DEFINE_SIMPLE_REDUCE_AGGREGATOR(Name, name) \ - struct Reduce##Name##Aggregator : ReduceAggregator { \ - Reduce##Name##Aggregator( \ - core::AggregationNode::Step step, \ - uint32_t inputIndex, \ - VectorPtr constant, \ - const TypePtr& resultType) \ - : ReduceAggregator(step, inputIndex, constant, resultType) {} \ - \ - std::unique_ptr doReduce( \ - cudf::table_view const& input, \ - TypePtr const& outputType, \ - rmm::cuda_stream_view stream, \ - vector_size_t /*inputRowCount*/) override { \ - auto const aggRequest = \ - cudf::make_##name##_aggregation(); \ - auto const cudfOutputType = \ - cudf_velox::veloxToCudfDataType(outputType); \ - auto const resultScalar = cudf::reduce( \ - input.column(inputIndex), \ - *aggRequest, \ - cudfOutputType, \ - stream, \ - get_temp_mr()); \ - return cudf::make_column_from_scalar( \ - *resultScalar, 1, stream, get_output_mr()); \ - } \ +#define DEFINE_SIMPLE_REDUCE_AGGREGATOR(Name, name) \ + struct Reduce##Name##Aggregator : ReduceAggregator { \ + Reduce##Name##Aggregator( \ + core::AggregationNode::Step step, \ + uint32_t inputIndex, \ + VectorPtr constant, \ + const TypePtr& resultType) \ + : ReduceAggregator(step, inputIndex, constant, resultType) {} \ + \ + std::unique_ptr doReduce( \ + cudf::table_view const& input, \ + TypePtr const& outputType, \ + rmm::cuda_stream_view stream, \ + vector_size_t /*inputRowCount*/) override { \ + auto const aggRequest = \ + cudf::make_##name##_aggregation(); \ + auto const cudfOutputType = cudf_velox::veloxToCudfDataType(outputType); \ + auto const resultScalar = cudf::reduce( \ + input.column(inputIndex), \ + *aggRequest, \ + cudfOutputType, \ + stream, \ + get_temp_mr()); \ + return cudf::make_column_from_scalar( \ + *resultScalar, 1, stream, get_output_mr()); \ + } \ }; DEFINE_SIMPLE_REDUCE_AGGREGATOR(Sum, sum) @@ -159,8 +158,7 @@ struct ReduceMeanAggregator : ReduceAggregator { case core::AggregationNode::Step::kSingle: { auto const aggRequest = cudf::make_mean_aggregation(); - auto const cudfOutputType = - cudf_velox::veloxToCudfDataType(outputType); + auto const cudfOutputType = cudf_velox::veloxToCudfDataType(outputType); auto const resultScalar = cudf::reduce( input.column(inputIndex), *aggRequest, @@ -175,10 +173,8 @@ struct ReduceMeanAggregator : ReduceAggregator { auto const& rowType = outputType->asRow(); auto const sumType = rowType.childAt(0); auto const countType = rowType.childAt(1); - auto const cudfSumType = - cudf_velox::veloxToCudfDataType(sumType); - auto const cudfCountType = - cudf_velox::veloxToCudfDataType(countType); + auto const cudfSumType = cudf_velox::veloxToCudfDataType(sumType); + auto const cudfCountType = cudf_velox::veloxToCudfDataType(countType); // sum auto const aggRequest = @@ -237,8 +233,7 @@ struct ReduceMeanAggregator : ReduceAggregator { countCol, *countAggRequest, countCol.type(), stream, get_temp_mr()); // divide the sums by the counts - auto const cudfOutputType = - cudf_velox::veloxToCudfDataType(outputType); + auto const cudfOutputType = cudf_velox::veloxToCudfDataType(outputType); return cudf::binary_operation( *sumResultCol, *countResultScalar, @@ -273,11 +268,7 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { cudf::make_sum_aggregation(); cudf::column_view inputCol = input.column(inputIndex); auto sumScalar = cudf::reduce( - inputCol, - *sumAgg, - inputCol.type(), - stream, - get_temp_mr()); + inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); auto countAgg = cudf::make_count_aggregation( cudf::null_policy::EXCLUDE); auto countScalar = cudf::reduce( @@ -286,8 +277,8 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, get_output_mr()); + auto sumCol = + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); auto countCol = cudf::make_column_from_scalar( *countScalar, 1, stream, get_output_mr()); return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); @@ -298,11 +289,7 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { cudf::column_view inputCol = input.column(inputIndex); if (step == core::AggregationNode::Step::kPartial) { auto sumScalar = cudf::reduce( - inputCol, - *aggRequest, - inputCol.type(), - stream, - get_temp_mr()); + inputCol, *aggRequest, inputCol.type(), stream, get_temp_mr()); auto countAgg = cudf::make_count_aggregation( cudf::null_policy::EXCLUDE); auto countScalar = cudf::reduce( @@ -311,15 +298,12 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, get_output_mr()); + auto sumCol = + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); auto countCol = cudf::make_column_from_scalar( *countScalar, 1, stream, get_output_mr()); return cudf_velox::serializeDecimalSumState( - sumCol->view(), - countCol->view(), - stream, - get_output_mr()); + sumCol->view(), countCol->view(), stream, get_output_mr()); } if (step == core::AggregationNode::Step::kIntermediate && inputCol.type().id() == cudf::type_id::STRING) { @@ -340,15 +324,12 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, get_output_mr()); + auto sumCol = + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); auto countCol = cudf::make_column_from_scalar( *countScalar, 1, stream, get_output_mr()); return cudf_velox::serializeDecimalSumState( - sumCol->view(), - countCol->view(), - stream, - get_output_mr()); + sumCol->view(), countCol->view(), stream, get_output_mr()); } if (step == core::AggregationNode::Step::kFinal && inputCol.type().id() == cudf::type_id::STRING) { @@ -381,12 +362,11 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); std::unique_ptr castedInput; if (outputType->isDecimal() && inputCol.type() != cudfOutType) { - castedInput = cudf::cast( - inputCol, cudfOutType, stream, get_output_mr()); + castedInput = cudf::cast(inputCol, cudfOutType, stream, get_output_mr()); inputCol = castedInput->view(); } - auto const resultScalar = cudf::reduce( - inputCol, *aggRequest, cudfOutType, stream, get_temp_mr()); + auto const resultScalar = + cudf::reduce(inputCol, *aggRequest, cudfOutType, stream, get_temp_mr()); return cudf::make_column_from_scalar( *resultScalar, 1, stream, get_output_mr()); } @@ -407,8 +387,7 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { sum->view(), count->view(), stream, get_output_mr()); auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); if (avgCol->type() != cudfOutType) { - avgCol = cudf::cast( - avgCol->view(), cudfOutType, stream, get_output_mr()); + avgCol = cudf::cast(avgCol->view(), cudfOutType, stream, get_output_mr()); } return avgCol; } From 7e68e643d92571e54c3652356f39d4796f563984 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Sat, 2 May 2026 14:43:17 -0700 Subject: [PATCH 07/64] Change ifs to CHECKs and tidy up flow, per @mattgara --- velox/experimental/cudf/exec/CudfGroupby.cpp | 14 ++++---------- velox/experimental/cudf/exec/CudfReduce.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 2cc654bca70..d2fb9a53efd 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -101,8 +101,8 @@ struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { cudf::table_view const& tbl, std::vector& requests, rmm::cuda_stream_view stream) override { - if (step == core::AggregationNode::Step::kIntermediate && - tbl.column(inputIndex).type().id() == cudf::type_id::STRING) { + if (step == core::AggregationNode::Step::kIntermediate) { + VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); auto scale = resultType->isDecimal() ? getDecimalPrecisionScale(*resultType).second : 0; @@ -122,11 +122,8 @@ struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { countRequest.values = decodedCount_->view(); countRequest.aggregations.push_back( cudf::make_sum_aggregation()); - return; - } - - if (step == core::AggregationNode::Step::kFinal && - tbl.column(inputIndex).type().id() == cudf::type_id::STRING) { + } else if (step == core::AggregationNode::Step::kFinal) { + VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); auto scale = getDecimalPrecisionScale(*resultType).second; if (isAvg_) { auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( @@ -145,7 +142,6 @@ struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { countRequest.values = decodedCount_->view(); countRequest.aggregations.push_back( cudf::make_sum_aggregation()); - return; } else { auto& request = requests.emplace_back(); sumIdx_ = requests.size() - 1; @@ -154,7 +150,6 @@ struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { request.values = decodedSum_->view(); request.aggregations.push_back( cudf::make_sum_aggregation()); - return; } } else { auto& request = requests.emplace_back(); @@ -168,7 +163,6 @@ struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { cudf::make_count_aggregation( cudf::null_policy::EXCLUDE)); } - return; } } diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 80c768e609d..e26514720a1 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -305,8 +305,8 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { return cudf_velox::serializeDecimalSumState( sumCol->view(), countCol->view(), stream, get_output_mr()); } - if (step == core::AggregationNode::Step::kIntermediate && - inputCol.type().id() == cudf::type_id::STRING) { + if (step == core::AggregationNode::Step::kIntermediate) { + VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); auto scale = outputType->isDecimal() ? getDecimalPrecisionScale(*outputType).second : 0; @@ -330,9 +330,8 @@ struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { *countScalar, 1, stream, get_output_mr()); return cudf_velox::serializeDecimalSumState( sumCol->view(), countCol->view(), stream, get_output_mr()); - } - if (step == core::AggregationNode::Step::kFinal && - inputCol.type().id() == cudf::type_id::STRING) { + } else if (step == core::AggregationNode::Step::kFinal) { + VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); auto scale = getDecimalPrecisionScale(*outputType).second; if (isAvg_) { auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( From ca006e472a7400f3b30bb46c5375c1f42c10f853 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Sat, 2 May 2026 14:44:00 -0700 Subject: [PATCH 08/64] Remove 'help' comment now that I understand --- velox/experimental/cudf/exec/AggregationRegistry.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/velox/experimental/cudf/exec/AggregationRegistry.cpp b/velox/experimental/cudf/exec/AggregationRegistry.cpp index dccc50811c0..1abb4faaf37 100644 --- a/velox/experimental/cudf/exec/AggregationRegistry.cpp +++ b/velox/experimental/cudf/exec/AggregationRegistry.cpp @@ -437,10 +437,6 @@ void registerCommonAggregationFunctions( .argumentType("row(double,bigint)") .build()}; - // WHY DOES SUM NOT HAVE THE EQUIVALENT OF THE ABOVE? - // THE ABOVE THEN CLASHES WITH BELOW - // @mattgara HELP! :) - // auto avgIntermediateSignatures = avgFinalIntermediateSignatures; avgIntermediateSignatures.insert( avgIntermediateSignatures.end(), decimalAvgIntermediate.begin(), From bf5260fadd4f7d340d07be7684d9f7d3f0754de1 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 11 May 2026 12:11:46 -0700 Subject: [PATCH 09/64] Fix after update from main --- velox/experimental/cudf/exec/CudfGroupby.cpp | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index eb9df0fc7b8..918528aa0bc 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -460,7 +460,8 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { void addGroupbyRequest( cudf::table_view const& tbl, - std::vector& requests) override { + std::vector& requests, + rmm::cuda_stream_view stream) override { auto& request = requests.emplace_back(); outputIdx_ = requests.size() - 1; request.values = tbl.column(inputIndex); @@ -510,12 +511,12 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { // Check if types already match expected output - avoid copies if so const auto& outputType = asRowType(resultType); - auto const cudfCountType = cudf::data_type( - cudf_velox::veloxToCudfTypeId(outputType->childAt(0))); - auto const cudfMeanType = cudf::data_type( - cudf_velox::veloxToCudfTypeId(outputType->childAt(1))); - auto const cudfM2Type = cudf::data_type( - cudf_velox::veloxToCudfTypeId(outputType->childAt(2))); + auto const cudfCountType = + cudf_velox::veloxToCudfDataType(outputType->childAt(0)); + auto const cudfMeanType = + cudf_velox::veloxToCudfDataType(outputType->childAt(1)); + auto const cudfM2Type = + cudf_velox::veloxToCudfDataType(outputType->childAt(2)); auto mergedView = merged->view(); bool typesMatch = mergedView.child(0).type() == cudfCountType && @@ -599,12 +600,11 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { rmm::cuda_stream_view stream) { const auto& outputType = asRowType(resultType); auto const cudfCountType = - cudf::data_type(cudf_velox::veloxToCudfTypeId(outputType->childAt(0))); + cudf_velox::veloxToCudfDataType(outputType->childAt(0)); auto const cudfMeanType = - cudf::data_type(cudf_velox::veloxToCudfTypeId(outputType->childAt(1))); + cudf_velox::veloxToCudfDataType(outputType->childAt(1)); auto const cudfM2Type = - cudf::data_type(cudf_velox::veloxToCudfTypeId(outputType->childAt(2))); - + cudf_velox::veloxToCudfDataType(outputType->childAt(2)); if (count->type() != cudfCountType) { count = cudf::cast(*count, cudfCountType, stream, get_output_mr()); } From 7f222b404c845b92898fcb56752ae38baf7033a7 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Sun, 3 May 2026 20:05:22 -0700 Subject: [PATCH 10/64] Cursor refactor to split up aggregator class (struct) (cherry picked from commit e0848145fc68eb151546dffc0a5cd87a3a051c48) --- velox/experimental/cudf/exec/CudfGroupby.cpp | 347 +++++++++++++------ 1 file changed, 235 insertions(+), 112 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 918528aa0bc..c50990050c1 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -88,82 +88,239 @@ DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Sum, sum, SUM) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Min, min, MIN) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Max, max, MAX) -struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { - GroupbyDecimalSumOrAvgAggregator( +struct DecimalSumCountRequestState { + uint32_t sumIdx{0}; + uint32_t countIdx{0}; + std::unique_ptr decodedSum; + std::unique_ptr decodedCount; +}; + +DecimalSumCountRequestState addDecimalSumCountRequestsAfterDecode( + cudf::column_view encodedColumn, + int32_t scale, + std::vector& requests, + rmm::cuda_stream_view stream) { + DecimalSumCountRequestState state; + auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + encodedColumn, scale, stream, cudf_velox::get_output_mr()); + state.decodedSum = std::move(decoded.sum); + state.decodedCount = std::move(decoded.count); + + state.sumIdx = requests.size(); + auto& sumRequest = requests.emplace_back(); + sumRequest.values = state.decodedSum->view(); + sumRequest.aggregations.push_back( + cudf::make_sum_aggregation()); + + state.countIdx = requests.size(); + auto& countRequest = requests.emplace_back(); + countRequest.values = state.decodedCount->view(); + countRequest.aggregations.push_back( + cudf::make_sum_aggregation()); + return state; +} + +DecimalSumCountRequestState addDecimalIntermediateSumCountRequests( + cudf::table_view const& tbl, + uint32_t inputIndex, + const TypePtr& resultType, + std::vector& requests, + rmm::cuda_stream_view stream) { + VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); + auto scale = resultType->isDecimal() + ? getDecimalPrecisionScale(*resultType).second + : 0; + return addDecimalSumCountRequestsAfterDecode( + tbl.column(inputIndex), scale, requests, stream); +} + +DecimalSumCountRequestState addDecimalFinalAvgSumCountRequests( + cudf::table_view const& tbl, + uint32_t inputIndex, + const TypePtr& resultType, + std::vector& requests, + rmm::cuda_stream_view stream) { + VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); + auto scale = getDecimalPrecisionScale(*resultType).second; + return addDecimalSumCountRequestsAfterDecode( + tbl.column(inputIndex), scale, requests, stream); +} + +void addDecimalFinalSumOnlyRequest( + cudf::table_view const& tbl, + uint32_t inputIndex, + const TypePtr& resultType, + std::vector& requests, + rmm::cuda_stream_view stream, + uint32_t& sumIdx, + std::unique_ptr& decodedSum) { + VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); + auto scale = getDecimalPrecisionScale(*resultType).second; + auto& request = requests.emplace_back(); + sumIdx = requests.size() - 1; + decodedSum = cudf_velox::deserializeDecimalSumState( + tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); + request.values = decodedSum->view(); + request.aggregations.push_back( + cudf::make_sum_aggregation()); +} + +void addDecimalRawPartialSingleSumRequest( + cudf::table_view const& tbl, + uint32_t inputIndex, + std::vector& requests, + bool includeCountAggregation, + uint32_t& sumIdx) { + auto& request = requests.emplace_back(); + sumIdx = requests.size() - 1; + request.values = tbl.column(inputIndex); + request.aggregations.push_back( + cudf::make_sum_aggregation()); + if (includeCountAggregation) { + request.aggregations.push_back( + cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE)); + } +} + +std::unique_ptr castCountColumnToInt64( + std::unique_ptr count, + rmm::cuda_stream_view stream) { + if (count->type().id() != cudf::type_id::INT64) { + count = cudf::cast( + *count, + cudf::data_type{cudf::type_id::INT64}, + stream, + cudf_velox::get_output_mr()); + } + return count; +} + +std::unique_ptr serializeDecimalPartialOrIntermediateState( + std::unique_ptr sum, + std::unique_ptr count, + rmm::cuda_stream_view stream) { + count = castCountColumnToInt64(std::move(count), stream); + return cudf_velox::serializeDecimalSumState( + sum->view(), count->view(), stream, cudf_velox::get_output_mr()); +} + +std::unique_ptr finalizeDecimalAverage( + std::unique_ptr sum, + std::unique_ptr count, + const TypePtr& resultType, + rmm::cuda_stream_view stream) { + count = castCountColumnToInt64(std::move(count), stream); + auto avgCol = cudf_velox::computeDecimalAverage( + sum->view(), count->view(), stream, cudf_velox::get_output_mr()); + auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); + if (avgCol->type() != cudfOutType) { + avgCol = cudf::cast( + avgCol->view(), cudfOutType, stream, cudf_velox::get_output_mr()); + } + return avgCol; +} + +struct GroupbyDecimalSumAggregator : GroupbyAggregator { + GroupbyDecimalSumAggregator( core::AggregationNode::Step step, uint32_t inputIndex, VectorPtr constant, - const TypePtr& resultType, - const bool isAvg) - : GroupbyAggregator(step, inputIndex, constant, resultType), - isAvg_(isAvg) {} + const TypePtr& resultType) + : GroupbyAggregator(step, inputIndex, constant, resultType) {} void addGroupbyRequest( cudf::table_view const& tbl, std::vector& requests, rmm::cuda_stream_view stream) override { if (step == core::AggregationNode::Step::kIntermediate) { - VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); - auto scale = resultType->isDecimal() - ? getDecimalPrecisionScale(*resultType).second - : 0; - auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( - tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); - decodedSum_ = std::move(decoded.sum); - decodedCount_ = std::move(decoded.count); - - sumIdx_ = requests.size(); - auto& sumRequest = requests.emplace_back(); - sumRequest.values = decodedSum_->view(); - sumRequest.aggregations.push_back( - cudf::make_sum_aggregation()); - - countIdx_ = requests.size(); - auto& countRequest = requests.emplace_back(); - countRequest.values = decodedCount_->view(); - countRequest.aggregations.push_back( - cudf::make_sum_aggregation()); + auto state = addDecimalIntermediateSumCountRequests( + tbl, inputIndex, resultType, requests, stream); + sumIdx_ = state.sumIdx; + countIdx_ = state.countIdx; + decodedSum_ = std::move(state.decodedSum); + decodedCount_ = std::move(state.decodedCount); } else if (step == core::AggregationNode::Step::kFinal) { - VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); - auto scale = getDecimalPrecisionScale(*resultType).second; - if (isAvg_) { - auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( - tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); - decodedSum_ = std::move(decoded.sum); - decodedCount_ = std::move(decoded.count); - - sumIdx_ = requests.size(); - auto& sumRequest = requests.emplace_back(); - sumRequest.values = decodedSum_->view(); - sumRequest.aggregations.push_back( - cudf::make_sum_aggregation()); + addDecimalFinalSumOnlyRequest( + tbl, + inputIndex, + resultType, + requests, + stream, + sumIdx_, + decodedSum_); + } else { + addDecimalRawPartialSingleSumRequest( + tbl, + inputIndex, + requests, + step == core::AggregationNode::Step::kPartial, + sumIdx_); + } + } - countIdx_ = requests.size(); - auto& countRequest = requests.emplace_back(); - countRequest.values = decodedCount_->view(); - countRequest.aggregations.push_back( - cudf::make_sum_aggregation()); - } else { - auto& request = requests.emplace_back(); - sumIdx_ = requests.size() - 1; - decodedSum_ = cudf_velox::deserializeDecimalSumState( - tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); - request.values = decodedSum_->view(); - request.aggregations.push_back( - cudf::make_sum_aggregation()); - } + std::unique_ptr makeOutputColumn( + std::vector& results, + rmm::cuda_stream_view stream) override { + auto col = std::move(results[sumIdx_].results[0]); + if (step == core::AggregationNode::Step::kPartial) { + auto count = std::move(results[sumIdx_].results[1]); + return serializeDecimalPartialOrIntermediateState( + std::move(col), std::move(count), stream); + } + if (step == core::AggregationNode::Step::kIntermediate) { + auto count = std::move(results[countIdx_].results[0]); + return serializeDecimalPartialOrIntermediateState( + std::move(col), std::move(count), stream); + } + auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); + if (col->type() != cudfResType) { + col = cudf::cast(*col, cudfResType, stream, cudf_velox::get_output_mr()); + } + return col; + } + + private: + uint32_t sumIdx_{0}; + uint32_t countIdx_{0}; + std::unique_ptr decodedSum_; + std::unique_ptr decodedCount_; +}; + +struct GroupbyDecimalAvgAggregator : GroupbyAggregator { + GroupbyDecimalAvgAggregator( + core::AggregationNode::Step step, + uint32_t inputIndex, + VectorPtr constant, + const TypePtr& resultType) + : GroupbyAggregator(step, inputIndex, constant, resultType) {} + + void addGroupbyRequest( + cudf::table_view const& tbl, + std::vector& requests, + rmm::cuda_stream_view stream) override { + if (step == core::AggregationNode::Step::kIntermediate) { + auto state = addDecimalIntermediateSumCountRequests( + tbl, inputIndex, resultType, requests, stream); + sumIdx_ = state.sumIdx; + countIdx_ = state.countIdx; + decodedSum_ = std::move(state.decodedSum); + decodedCount_ = std::move(state.decodedCount); + } else if (step == core::AggregationNode::Step::kFinal) { + auto state = addDecimalFinalAvgSumCountRequests( + tbl, inputIndex, resultType, requests, stream); + sumIdx_ = state.sumIdx; + countIdx_ = state.countIdx; + decodedSum_ = std::move(state.decodedSum); + decodedCount_ = std::move(state.decodedCount); } else { - auto& request = requests.emplace_back(); - sumIdx_ = requests.size() - 1; - request.values = tbl.column(inputIndex); - request.aggregations.push_back( - cudf::make_sum_aggregation()); - if (step == core::AggregationNode::Step::kPartial || - (step == core::AggregationNode::Step::kSingle && isAvg_)) { - request.aggregations.push_back( - cudf::make_count_aggregation( - cudf::null_policy::EXCLUDE)); - } + addDecimalRawPartialSingleSumRequest( + tbl, + inputIndex, + requests, + step == core::AggregationNode::Step::kPartial || + step == core::AggregationNode::Step::kSingle, + sumIdx_); } } @@ -171,37 +328,25 @@ struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { std::vector& results, rmm::cuda_stream_view stream) override { auto col = std::move(results[sumIdx_].results[0]); - if (isAvg_ && step == core::AggregationNode::Step::kSingle) { + if (step == core::AggregationNode::Step::kSingle) { auto count = std::move(results[sumIdx_].results[1]); - return computeAvgColumn(std::move(col), std::move(count), stream); + return finalizeDecimalAverage( + std::move(col), std::move(count), resultType, stream); } if (step == core::AggregationNode::Step::kPartial) { auto count = std::move(results[sumIdx_].results[1]); - if (count->type().id() != cudf::type_id::INT64) { - count = cudf::cast( - *count, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_output_mr()); - } - return cudf_velox::serializeDecimalSumState( - col->view(), count->view(), stream, cudf_velox::get_output_mr()); + return serializeDecimalPartialOrIntermediateState( + std::move(col), std::move(count), stream); } if (step == core::AggregationNode::Step::kIntermediate) { auto count = std::move(results[countIdx_].results[0]); - if (count->type().id() != cudf::type_id::INT64) { - count = cudf::cast( - *count, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_output_mr()); - } - return cudf_velox::serializeDecimalSumState( - col->view(), count->view(), stream, cudf_velox::get_output_mr()); + return serializeDecimalPartialOrIntermediateState( + std::move(col), std::move(count), stream); } - if (isAvg_ && step == core::AggregationNode::Step::kFinal) { + if (step == core::AggregationNode::Step::kFinal) { auto count = std::move(results[countIdx_].results[0]); - return computeAvgColumn(std::move(col), std::move(count), stream); + return finalizeDecimalAverage( + std::move(col), std::move(count), resultType, stream); } auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); if (col->type() != cudfResType) { @@ -211,30 +356,8 @@ struct GroupbyDecimalSumOrAvgAggregator : GroupbyAggregator { } private: - std::unique_ptr computeAvgColumn( - std::unique_ptr sum, - std::unique_ptr count, - rmm::cuda_stream_view stream) const { - if (count->type().id() != cudf::type_id::INT64) { - count = cudf::cast( - *count, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_output_mr()); - } - auto avgCol = cudf_velox::computeDecimalAverage( - sum->view(), count->view(), stream, cudf_velox::get_output_mr()); - auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); - if (avgCol->type() != cudfOutType) { - avgCol = cudf::cast( - avgCol->view(), cudfOutType, stream, cudf_velox::get_output_mr()); - } - return avgCol; - } - uint32_t sumIdx_{0}; uint32_t countIdx_{0}; - const bool isAvg_{false}; std::unique_ptr decodedSum_; std::unique_ptr decodedCount_; }; @@ -639,8 +762,8 @@ std::unique_ptr createGroupbyAggregator( auto prefix = cudf_velox::CudfConfig::getInstance().functionNamePrefix; if (kind.rfind(prefix + "sum", 0) == 0) { if (p.isDecimalInput) { - return std::make_unique( - p.companionStep, p.inputIndex, p.constant, p.resultType, false); + return std::make_unique( + p.companionStep, p.inputIndex, p.constant, p.resultType); } return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); @@ -656,8 +779,8 @@ std::unique_ptr createGroupbyAggregator( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "avg", 0) == 0) { if (p.isDecimalInput) { - return std::make_unique( - p.companionStep, p.inputIndex, p.constant, p.resultType, true); + return std::make_unique( + p.companionStep, p.inputIndex, p.constant, p.resultType); } return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); From 890d792a6d75fddf1609bdcb1b27a85f03bf8c62 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 4 May 2026 14:43:37 -0700 Subject: [PATCH 11/64] Cursor refactor to split up Reduce aggegator in a similar way (cherry picked from commit 7a22e05fba2601f5e23fbcbc818ae1effed1da36) --- velox/experimental/cudf/exec/CudfReduce.cpp | 367 +++++++++++++------- 1 file changed, 235 insertions(+), 132 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index e26514720a1..ce922c01111 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -248,150 +248,253 @@ struct ReduceMeanAggregator : ReduceAggregator { } }; -struct ReduceDecimalSumOrAvgAggregator : ReduceAggregator { - ReduceDecimalSumOrAvgAggregator( +std::unique_ptr castCountColumnToInt64( + std::unique_ptr count, + rmm::cuda_stream_view stream) { + if (count->type().id() != cudf::type_id::INT64) { + count = cudf::cast( + *count, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_output_mr()); + } + return count; +} + +std::unique_ptr serializeDecimalPartialOrIntermediateState( + std::unique_ptr sum, + std::unique_ptr count, + rmm::cuda_stream_view stream) { + count = castCountColumnToInt64(std::move(count), stream); + return cudf_velox::serializeDecimalSumState( + sum->view(), count->view(), stream, get_output_mr()); +} + +std::unique_ptr finalizeDecimalAverage( + std::unique_ptr sum, + std::unique_ptr count, + TypePtr const& resultType, + rmm::cuda_stream_view stream) { + count = castCountColumnToInt64(std::move(count), stream); + auto avgCol = cudf_velox::computeDecimalAverage( + sum->view(), count->view(), stream, get_output_mr()); + auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); + if (avgCol->type() != cudfOutType) { + avgCol = + cudf::cast(avgCol->view(), cudfOutType, stream, get_output_mr()); + } + return avgCol; +} + +std::unique_ptr partialDecimalSumCountToSerializedString( + cudf::column_view inputCol, + rmm::cuda_stream_view stream) { + auto const sumAgg = + cudf::make_sum_aggregation(); + auto sumScalar = + cudf::reduce(inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); + auto countAgg = cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE); + auto countScalar = cudf::reduce( + inputCol, + *countAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + auto countCol = + cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + return serializeDecimalPartialOrIntermediateState( + std::move(sumCol), std::move(countCol), stream); +} + +std::unique_ptr intermediateDecimalMergeSerializedString( + cudf::column_view inputCol, + int32_t scale, + rmm::cuda_stream_view stream) { + auto const sumAgg = + cudf::make_sum_aggregation(); + auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + inputCol, scale, stream, get_output_mr()); + auto sumScalar = cudf::reduce( + decoded.sum->view(), + *sumAgg, + decoded.sum->view().type(), + stream, + get_temp_mr()); + auto countScalar = cudf::reduce( + decoded.count->view(), + *sumAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + auto countCol = + cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + return serializeDecimalPartialOrIntermediateState( + std::move(sumCol), std::move(countCol), stream); +} + +std::unique_ptr finalDecimalAvgFromSerializedString( + cudf::column_view inputCol, + int32_t scale, + TypePtr const& resultType, + rmm::cuda_stream_view stream) { + auto const sumAgg = + cudf::make_sum_aggregation(); + auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( + inputCol, scale, stream, get_output_mr()); + auto sumScalar = cudf::reduce( + sumAndCount.sum->view(), + *sumAgg, + sumAndCount.sum->view().type(), + stream, + get_temp_mr()); + auto countScalar = cudf::reduce( + sumAndCount.count->view(), + *sumAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + auto countCol = + cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + return finalizeDecimalAverage( + std::move(sumCol), std::move(countCol), resultType, stream); +} + +std::unique_ptr finalDecimalSumDecodeColumn( + cudf::column_view inputCol, + int32_t scale, + rmm::cuda_stream_view stream) { + return cudf_velox::deserializeDecimalSumState( + inputCol, scale, stream, get_output_mr()); +} + +std::unique_ptr singleDecimalAvgFromRawColumn( + cudf::column_view inputCol, + TypePtr const& resultType, + rmm::cuda_stream_view stream) { + auto const sumAgg = + cudf::make_sum_aggregation(); + auto sumScalar = + cudf::reduce(inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); + auto countAgg = cudf::make_count_aggregation( + cudf::null_policy::EXCLUDE); + auto countScalar = cudf::reduce( + inputCol, + *countAgg, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_temp_mr()); + auto sumCol = + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + auto countCol = + cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + return finalizeDecimalAverage( + std::move(sumCol), std::move(countCol), resultType, stream); +} + +std::unique_ptr singleOrRawDecimalSumWithCast( + cudf::column_view inputCol, + TypePtr const& outputType, + rmm::cuda_stream_view stream) { + auto const sumAgg = + cudf::make_sum_aggregation(); + auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); + std::unique_ptr castedInput; + if (outputType->isDecimal() && inputCol.type() != cudfOutType) { + castedInput = cudf::cast(inputCol, cudfOutType, stream, get_output_mr()); + inputCol = castedInput->view(); + } + auto const resultScalar = + cudf::reduce(inputCol, *sumAgg, cudfOutType, stream, get_temp_mr()); + return cudf::make_column_from_scalar( + *resultScalar, 1, stream, get_output_mr()); +} + +struct ReduceDecimalSumAggregator : ReduceAggregator { + ReduceDecimalSumAggregator( core::AggregationNode::Step step, uint32_t inputIndex, VectorPtr constant, - const TypePtr& resultType, - bool isAvg) - : ReduceAggregator(step, inputIndex, constant, resultType), - isAvg_(isAvg) {} + const TypePtr& resultType) + : ReduceAggregator(step, inputIndex, constant, resultType) {} std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, rmm::cuda_stream_view stream, vector_size_t /*inputRowCount*/) override { - if (step == core::AggregationNode::Step::kSingle && isAvg_) { - auto const sumAgg = - cudf::make_sum_aggregation(); - cudf::column_view inputCol = input.column(inputIndex); - auto sumScalar = cudf::reduce( - inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); - auto countAgg = cudf::make_count_aggregation( - cudf::null_policy::EXCLUDE); - auto countScalar = cudf::reduce( - inputCol, - *countAgg, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_temp_mr()); - auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, get_output_mr()); - return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); - } - std::unique_ptr stringDecodedSum; - auto const aggRequest = - cudf::make_sum_aggregation(); cudf::column_view inputCol = input.column(inputIndex); - if (step == core::AggregationNode::Step::kPartial) { - auto sumScalar = cudf::reduce( - inputCol, *aggRequest, inputCol.type(), stream, get_temp_mr()); - auto countAgg = cudf::make_count_aggregation( - cudf::null_policy::EXCLUDE); - auto countScalar = cudf::reduce( - inputCol, - *countAgg, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_temp_mr()); - auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, get_output_mr()); - return cudf_velox::serializeDecimalSumState( - sumCol->view(), countCol->view(), stream, get_output_mr()); - } - if (step == core::AggregationNode::Step::kIntermediate) { - VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); - auto scale = outputType->isDecimal() - ? getDecimalPrecisionScale(*outputType).second - : 0; - auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( - inputCol, scale, stream, get_output_mr()); - auto sumScalar = cudf::reduce( - decoded.sum->view(), - *aggRequest, - decoded.sum->view().type(), - stream, - get_temp_mr()); - auto countScalar = cudf::reduce( - decoded.count->view(), - *aggRequest, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_temp_mr()); - auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, get_output_mr()); - return cudf_velox::serializeDecimalSumState( - sumCol->view(), countCol->view(), stream, get_output_mr()); - } else if (step == core::AggregationNode::Step::kFinal) { - VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); - auto scale = getDecimalPrecisionScale(*outputType).second; - if (isAvg_) { - auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( - inputCol, scale, stream, get_output_mr()); - auto sumScalar = cudf::reduce( - sumAndCount.sum->view(), - *aggRequest, - sumAndCount.sum->view().type(), - stream, - get_temp_mr()); - auto countScalar = cudf::reduce( - sumAndCount.count->view(), - *aggRequest, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumScalar, 1, stream, get_output_mr()); - auto countCol = cudf::make_column_from_scalar( - *countScalar, 1, stream, get_output_mr()); - return computeAvgColumn(std::move(sumCol), std::move(countCol), stream); + switch (step) { + case core::AggregationNode::Step::kSingle: + return singleOrRawDecimalSumWithCast(inputCol, outputType, stream); + case core::AggregationNode::Step::kPartial: + return partialDecimalSumCountToSerializedString(inputCol, stream); + case core::AggregationNode::Step::kIntermediate: { + VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); + auto scale = outputType->isDecimal() + ? getDecimalPrecisionScale(*outputType).second + : 0; + return intermediateDecimalMergeSerializedString( + inputCol, scale, stream); } - stringDecodedSum = cudf_velox::deserializeDecimalSumState( - inputCol, scale, stream, get_output_mr()); - inputCol = stringDecodedSum->view(); - } - auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); - std::unique_ptr castedInput; - if (outputType->isDecimal() && inputCol.type() != cudfOutType) { - castedInput = cudf::cast(inputCol, cudfOutType, stream, get_output_mr()); - inputCol = castedInput->view(); + case core::AggregationNode::Step::kFinal: { + VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); + auto scale = getDecimalPrecisionScale(*outputType).second; + auto decodedSum = + finalDecimalSumDecodeColumn(inputCol, scale, stream); + return singleOrRawDecimalSumWithCast( + decodedSum->view(), outputType, stream); + } + default: + VELOX_NYI("Unsupported aggregation step for decimal sum reduce"); } - auto const resultScalar = - cudf::reduce(inputCol, *aggRequest, cudfOutType, stream, get_temp_mr()); - return cudf::make_column_from_scalar( - *resultScalar, 1, stream, get_output_mr()); } +}; - private: - std::unique_ptr computeAvgColumn( - std::unique_ptr sum, - std::unique_ptr count, - rmm::cuda_stream_view stream) const { - if (count->type().id() != cudf::type_id::INT64) { - count = cudf::cast( - *count, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_output_mr()); - } - auto avgCol = cudf_velox::computeDecimalAverage( - sum->view(), count->view(), stream, get_output_mr()); - auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); - if (avgCol->type() != cudfOutType) { - avgCol = cudf::cast(avgCol->view(), cudfOutType, stream, get_output_mr()); +struct ReduceDecimalAvgAggregator : ReduceAggregator { + ReduceDecimalAvgAggregator( + core::AggregationNode::Step step, + uint32_t inputIndex, + VectorPtr constant, + const TypePtr& resultType) + : ReduceAggregator(step, inputIndex, constant, resultType) {} + + std::unique_ptr doReduce( + cudf::table_view const& input, + TypePtr const& outputType, + rmm::cuda_stream_view stream, + vector_size_t /*inputRowCount*/) override { + cudf::column_view inputCol = input.column(inputIndex); + switch (step) { + case core::AggregationNode::Step::kSingle: + return singleDecimalAvgFromRawColumn( + inputCol, resultType, stream); + case core::AggregationNode::Step::kPartial: + return partialDecimalSumCountToSerializedString(inputCol, stream); + case core::AggregationNode::Step::kIntermediate: { + VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); + auto scale = outputType->isDecimal() + ? getDecimalPrecisionScale(*outputType).second + : 0; + return intermediateDecimalMergeSerializedString( + inputCol, scale, stream); + } + case core::AggregationNode::Step::kFinal: { + VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); + auto scale = getDecimalPrecisionScale(*outputType).second; + return finalDecimalAvgFromSerializedString( + inputCol, scale, resultType, stream); + } + default: + VELOX_NYI("Unsupported aggregation step for decimal avg reduce"); } - return avgCol; } - - const bool isAvg_{false}; }; struct ApproxDistinctAggregator : ReduceAggregator { @@ -595,8 +698,8 @@ std::unique_ptr createReduceAggregator( auto prefix = cudf_velox::CudfConfig::getInstance().functionNamePrefix; if (kind.rfind(prefix + "sum", 0) == 0) { if (p.isDecimalInput) { - return std::make_unique( - p.companionStep, p.inputIndex, p.constant, p.resultType, false); + return std::make_unique( + p.companionStep, p.inputIndex, p.constant, p.resultType); } return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); @@ -612,8 +715,8 @@ std::unique_ptr createReduceAggregator( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "avg", 0) == 0) { if (p.isDecimalInput) { - return std::make_unique( - p.companionStep, p.inputIndex, p.constant, p.resultType, true); + return std::make_unique( + p.companionStep, p.inputIndex, p.constant, p.resultType); } return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); From 837b46396e1ae5b4df5c5eaa0516fd80e492d84b Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 4 May 2026 14:48:48 -0700 Subject: [PATCH 12/64] Factor out common helper functions (cherry picked from commit d4eaf31e4fec2367fc462e6f9a1299665d97adab) --- velox/experimental/cudf/exec/CMakeLists.txt | 1 + velox/experimental/cudf/exec/CudfGroupby.cpp | 41 +----------- velox/experimental/cudf/exec/CudfReduce.cpp | 41 +----------- .../cudf/exec/DecimalAggregationCommon.cpp | 65 +++++++++++++++++++ .../cudf/exec/DecimalAggregationCommon.h | 43 ++++++++++++ 5 files changed, 115 insertions(+), 76 deletions(-) create mode 100644 velox/experimental/cudf/exec/DecimalAggregationCommon.cpp create mode 100644 velox/experimental/cudf/exec/DecimalAggregationCommon.h diff --git a/velox/experimental/cudf/exec/CMakeLists.txt b/velox/experimental/cudf/exec/CMakeLists.txt index c60bef5a278..a0a68811d37 100644 --- a/velox/experimental/cudf/exec/CMakeLists.txt +++ b/velox/experimental/cudf/exec/CMakeLists.txt @@ -31,6 +31,7 @@ add_library( CudfOrderBy.cpp CudfTopN.cpp DebugUtil.cpp + DecimalAggregationCommon.cpp DecimalAggregationKernels.cu GpuResources.cpp OperatorAdapters.cpp diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index c50990050c1..080e656cc85 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -18,6 +18,7 @@ #include "velox/experimental/cudf/CudfNoDefaults.h" #include "velox/experimental/cudf/exec/CudfFilterProject.h" #include "velox/experimental/cudf/exec/CudfGroupby.h" +#include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/Utilities.h" @@ -41,10 +42,12 @@ namespace { using namespace facebook::velox; using cudf_velox::CountInputKind; +using cudf_velox::finalizeDecimalAverage; using cudf_velox::get_output_mr; using cudf_velox::get_temp_mr; using cudf_velox::GroupbyAggregator; using cudf_velox::ResolvedAggregateInfo; +using cudf_velox::serializeDecimalPartialOrIntermediateState; #define DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Name, name, KIND) \ struct Groupby##Name##Aggregator : GroupbyAggregator { \ @@ -183,44 +186,6 @@ void addDecimalRawPartialSingleSumRequest( } } -std::unique_ptr castCountColumnToInt64( - std::unique_ptr count, - rmm::cuda_stream_view stream) { - if (count->type().id() != cudf::type_id::INT64) { - count = cudf::cast( - *count, - cudf::data_type{cudf::type_id::INT64}, - stream, - cudf_velox::get_output_mr()); - } - return count; -} - -std::unique_ptr serializeDecimalPartialOrIntermediateState( - std::unique_ptr sum, - std::unique_ptr count, - rmm::cuda_stream_view stream) { - count = castCountColumnToInt64(std::move(count), stream); - return cudf_velox::serializeDecimalSumState( - sum->view(), count->view(), stream, cudf_velox::get_output_mr()); -} - -std::unique_ptr finalizeDecimalAverage( - std::unique_ptr sum, - std::unique_ptr count, - const TypePtr& resultType, - rmm::cuda_stream_view stream) { - count = castCountColumnToInt64(std::move(count), stream); - auto avgCol = cudf_velox::computeDecimalAverage( - sum->view(), count->view(), stream, cudf_velox::get_output_mr()); - auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); - if (avgCol->type() != cudfOutType) { - avgCol = cudf::cast( - avgCol->view(), cudfOutType, stream, cudf_velox::get_output_mr()); - } - return avgCol; -} - struct GroupbyDecimalSumAggregator : GroupbyAggregator { GroupbyDecimalSumAggregator( core::AggregationNode::Step step, diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index ce922c01111..7c1b4c268ba 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -19,6 +19,7 @@ #include "velox/experimental/cudf/exec/CudfAggregation.h" #include "velox/experimental/cudf/exec/CudfFilterProject.h" #include "velox/experimental/cudf/exec/CudfReduce.h" +#include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/Utilities.h" @@ -42,8 +43,10 @@ namespace { using namespace facebook::velox; using facebook::velox::cudf_velox::CountInputKind; +using facebook::velox::cudf_velox::finalizeDecimalAverage; using facebook::velox::cudf_velox::get_output_mr; using facebook::velox::cudf_velox::get_temp_mr; +using facebook::velox::cudf_velox::serializeDecimalPartialOrIntermediateState; using facebook::velox::cudf_velox::ReduceAggregator; using facebook::velox::cudf_velox::ResolvedAggregateInfo; @@ -248,44 +251,6 @@ struct ReduceMeanAggregator : ReduceAggregator { } }; -std::unique_ptr castCountColumnToInt64( - std::unique_ptr count, - rmm::cuda_stream_view stream) { - if (count->type().id() != cudf::type_id::INT64) { - count = cudf::cast( - *count, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_output_mr()); - } - return count; -} - -std::unique_ptr serializeDecimalPartialOrIntermediateState( - std::unique_ptr sum, - std::unique_ptr count, - rmm::cuda_stream_view stream) { - count = castCountColumnToInt64(std::move(count), stream); - return cudf_velox::serializeDecimalSumState( - sum->view(), count->view(), stream, get_output_mr()); -} - -std::unique_ptr finalizeDecimalAverage( - std::unique_ptr sum, - std::unique_ptr count, - TypePtr const& resultType, - rmm::cuda_stream_view stream) { - count = castCountColumnToInt64(std::move(count), stream); - auto avgCol = cudf_velox::computeDecimalAverage( - sum->view(), count->view(), stream, get_output_mr()); - auto const cudfOutType = cudf_velox::veloxToCudfDataType(resultType); - if (avgCol->type() != cudfOutType) { - avgCol = - cudf::cast(avgCol->view(), cudfOutType, stream, get_output_mr()); - } - return avgCol; -} - std::unique_ptr partialDecimalSumCountToSerializedString( cudf::column_view inputCol, rmm::cuda_stream_view stream) { diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp new file mode 100644 index 00000000000..f71d4e9e752 --- /dev/null +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/experimental/cudf/exec/DecimalAggregationCommon.h" + +#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" +#include "velox/experimental/cudf/exec/GpuResources.h" +#include "velox/experimental/cudf/exec/VeloxCudfInterop.h" + +#include + +namespace facebook::velox::cudf_velox { + +std::unique_ptr castCountColumnToInt64( + std::unique_ptr count, + rmm::cuda_stream_view stream) { + if (count->type().id() != cudf::type_id::INT64) { + count = cudf::cast( + *count, + cudf::data_type{cudf::type_id::INT64}, + stream, + get_output_mr()); + } + return count; +} + +std::unique_ptr serializeDecimalPartialOrIntermediateState( + std::unique_ptr sum, + std::unique_ptr count, + rmm::cuda_stream_view stream) { + count = castCountColumnToInt64(std::move(count), stream); + return serializeDecimalSumState( + sum->view(), count->view(), stream, get_output_mr()); +} + +std::unique_ptr finalizeDecimalAverage( + std::unique_ptr sum, + std::unique_ptr count, + const TypePtr& resultType, + rmm::cuda_stream_view stream) { + count = castCountColumnToInt64(std::move(count), stream); + auto avgCol = computeDecimalAverage( + sum->view(), count->view(), stream, get_output_mr()); + auto const cudfOutType = veloxToCudfDataType(resultType); + if (avgCol->type() != cudfOutType) { + avgCol = cudf::cast( + avgCol->view(), cudfOutType, stream, get_output_mr()); + } + return avgCol; +} + +} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.h b/velox/experimental/cudf/exec/DecimalAggregationCommon.h new file mode 100644 index 00000000000..974c19a0c3d --- /dev/null +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/type/Type.h" + +#include + +#include + +#include + +namespace facebook::velox::cudf_velox { + +std::unique_ptr castCountColumnToInt64( + std::unique_ptr count, + rmm::cuda_stream_view stream); + +std::unique_ptr serializeDecimalPartialOrIntermediateState( + std::unique_ptr sum, + std::unique_ptr count, + rmm::cuda_stream_view stream); + +std::unique_ptr finalizeDecimalAverage( + std::unique_ptr sum, + std::unique_ptr count, + const TypePtr& resultType, + rmm::cuda_stream_view stream); + +} // namespace facebook::velox::cudf_velox From 30897a6c51bab9e9b22a1ddb6f2066abdbea290b Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 11 May 2026 10:38:42 -0700 Subject: [PATCH 13/64] Refactor to remove state struct and associated unpacking at call sites (cherry picked from commit f3e6be928ed7bdc0650645232936acf77fe4be61) --- velox/experimental/cudf/exec/CudfGroupby.cpp | 115 ++++++++++++------- 1 file changed, 72 insertions(+), 43 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 080e656cc85..d7cd7b4bb79 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -91,62 +91,79 @@ DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Sum, sum, SUM) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Min, min, MIN) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Max, max, MAX) -struct DecimalSumCountRequestState { - uint32_t sumIdx{0}; - uint32_t countIdx{0}; - std::unique_ptr decodedSum; - std::unique_ptr decodedCount; -}; - -DecimalSumCountRequestState addDecimalSumCountRequestsAfterDecode( +void addDecimalSumCountRequestsAfterDecode( cudf::column_view encodedColumn, int32_t scale, std::vector& requests, - rmm::cuda_stream_view stream) { - DecimalSumCountRequestState state; + rmm::cuda_stream_view stream, + uint32_t& sumIdx, + uint32_t& countIdx, + std::unique_ptr& decodedSum, + std::unique_ptr& decodedCount) { auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( encodedColumn, scale, stream, cudf_velox::get_output_mr()); - state.decodedSum = std::move(decoded.sum); - state.decodedCount = std::move(decoded.count); + decodedSum = std::move(decoded.sum); + decodedCount = std::move(decoded.count); - state.sumIdx = requests.size(); + sumIdx = requests.size(); auto& sumRequest = requests.emplace_back(); - sumRequest.values = state.decodedSum->view(); + sumRequest.values = decodedSum->view(); sumRequest.aggregations.push_back( cudf::make_sum_aggregation()); - state.countIdx = requests.size(); + countIdx = requests.size(); auto& countRequest = requests.emplace_back(); - countRequest.values = state.decodedCount->view(); + countRequest.values = decodedCount->view(); countRequest.aggregations.push_back( cudf::make_sum_aggregation()); - return state; } -DecimalSumCountRequestState addDecimalIntermediateSumCountRequests( +void addDecimalIntermediateSumCountRequests( cudf::table_view const& tbl, uint32_t inputIndex, const TypePtr& resultType, std::vector& requests, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + uint32_t& sumIdx, + uint32_t& countIdx, + std::unique_ptr& decodedSum, + std::unique_ptr& decodedCount) { VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); auto scale = resultType->isDecimal() ? getDecimalPrecisionScale(*resultType).second : 0; - return addDecimalSumCountRequestsAfterDecode( - tbl.column(inputIndex), scale, requests, stream); + addDecimalSumCountRequestsAfterDecode( + tbl.column(inputIndex), + scale, + requests, + stream, + sumIdx, + countIdx, + decodedSum, + decodedCount); } -DecimalSumCountRequestState addDecimalFinalAvgSumCountRequests( +void addDecimalFinalAvgSumCountRequests( cudf::table_view const& tbl, uint32_t inputIndex, const TypePtr& resultType, std::vector& requests, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + uint32_t& sumIdx, + uint32_t& countIdx, + std::unique_ptr& decodedSum, + std::unique_ptr& decodedCount) { VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); auto scale = getDecimalPrecisionScale(*resultType).second; - return addDecimalSumCountRequestsAfterDecode( - tbl.column(inputIndex), scale, requests, stream); + addDecimalSumCountRequestsAfterDecode( + tbl.column(inputIndex), + scale, + requests, + stream, + sumIdx, + countIdx, + decodedSum, + decodedCount); } void addDecimalFinalSumOnlyRequest( @@ -199,12 +216,16 @@ struct GroupbyDecimalSumAggregator : GroupbyAggregator { std::vector& requests, rmm::cuda_stream_view stream) override { if (step == core::AggregationNode::Step::kIntermediate) { - auto state = addDecimalIntermediateSumCountRequests( - tbl, inputIndex, resultType, requests, stream); - sumIdx_ = state.sumIdx; - countIdx_ = state.countIdx; - decodedSum_ = std::move(state.decodedSum); - decodedCount_ = std::move(state.decodedCount); + addDecimalIntermediateSumCountRequests( + tbl, + inputIndex, + resultType, + requests, + stream, + sumIdx_, + countIdx_, + decodedSum_, + decodedCount_); } else if (step == core::AggregationNode::Step::kFinal) { addDecimalFinalSumOnlyRequest( tbl, @@ -265,19 +286,27 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { std::vector& requests, rmm::cuda_stream_view stream) override { if (step == core::AggregationNode::Step::kIntermediate) { - auto state = addDecimalIntermediateSumCountRequests( - tbl, inputIndex, resultType, requests, stream); - sumIdx_ = state.sumIdx; - countIdx_ = state.countIdx; - decodedSum_ = std::move(state.decodedSum); - decodedCount_ = std::move(state.decodedCount); + addDecimalIntermediateSumCountRequests( + tbl, + inputIndex, + resultType, + requests, + stream, + sumIdx_, + countIdx_, + decodedSum_, + decodedCount_); } else if (step == core::AggregationNode::Step::kFinal) { - auto state = addDecimalFinalAvgSumCountRequests( - tbl, inputIndex, resultType, requests, stream); - sumIdx_ = state.sumIdx; - countIdx_ = state.countIdx; - decodedSum_ = std::move(state.decodedSum); - decodedCount_ = std::move(state.decodedCount); + addDecimalFinalAvgSumCountRequests( + tbl, + inputIndex, + resultType, + requests, + stream, + sumIdx_, + countIdx_, + decodedSum_, + decodedCount_); } else { addDecimalRawPartialSingleSumRequest( tbl, From 48575c174e1f3476ceedd9377d78f7bf5abb9c2c Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 11 May 2026 13:25:15 -0700 Subject: [PATCH 14/64] Factor out remaining case body code to separate functions for clarity, reimplement column type validation and move to shared source (cherry picked from commit 32061973517181a3284d8577512f15b96d24d1d0) --- velox/experimental/cudf/exec/CudfGroupby.cpp | 7 +- velox/experimental/cudf/exec/CudfReduce.cpp | 76 +++++++++++-------- .../cudf/exec/DecimalAggregationCommon.cpp | 7 ++ .../cudf/exec/DecimalAggregationCommon.h | 4 + 4 files changed, 61 insertions(+), 33 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index d7cd7b4bb79..f91b702e1e3 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -48,6 +48,7 @@ using cudf_velox::get_temp_mr; using cudf_velox::GroupbyAggregator; using cudf_velox::ResolvedAggregateInfo; using cudf_velox::serializeDecimalPartialOrIntermediateState; +using cudf_velox::validateIntermediateColumnType; #define DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Name, name, KIND) \ struct Groupby##Name##Aggregator : GroupbyAggregator { \ @@ -128,7 +129,7 @@ void addDecimalIntermediateSumCountRequests( uint32_t& countIdx, std::unique_ptr& decodedSum, std::unique_ptr& decodedCount) { - VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); + validateIntermediateColumnType(tbl.column(inputIndex)); auto scale = resultType->isDecimal() ? getDecimalPrecisionScale(*resultType).second : 0; @@ -153,7 +154,7 @@ void addDecimalFinalAvgSumCountRequests( uint32_t& countIdx, std::unique_ptr& decodedSum, std::unique_ptr& decodedCount) { - VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); + validateIntermediateColumnType(tbl.column(inputIndex)); auto scale = getDecimalPrecisionScale(*resultType).second; addDecimalSumCountRequestsAfterDecode( tbl.column(inputIndex), @@ -174,7 +175,7 @@ void addDecimalFinalSumOnlyRequest( rmm::cuda_stream_view stream, uint32_t& sumIdx, std::unique_ptr& decodedSum) { - VELOX_CHECK(tbl.column(inputIndex).type().id() == cudf::type_id::STRING); + validateIntermediateColumnType(tbl.column(inputIndex)); auto scale = getDecimalPrecisionScale(*resultType).second; auto& request = requests.emplace_back(); sumIdx = requests.size() - 1; diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 7c1b4c268ba..0cbe542f1bd 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -47,6 +47,7 @@ using facebook::velox::cudf_velox::finalizeDecimalAverage; using facebook::velox::cudf_velox::get_output_mr; using facebook::velox::cudf_velox::get_temp_mr; using facebook::velox::cudf_velox::serializeDecimalPartialOrIntermediateState; +using facebook::velox::cudf_velox::validateIntermediateColumnType; using facebook::velox::cudf_velox::ReduceAggregator; using facebook::velox::cudf_velox::ResolvedAggregateInfo; @@ -381,6 +382,39 @@ std::unique_ptr singleOrRawDecimalSumWithCast( *resultScalar, 1, stream, get_output_mr()); } +std::unique_ptr reduceIntermediateDecimalFromSerializedColumn( + cudf::column_view inputCol, + TypePtr const& outputType, + rmm::cuda_stream_view stream) { + validateIntermediateColumnType(inputCol); + auto scale = outputType->isDecimal() + ? getDecimalPrecisionScale(*outputType).second + : 0; + return intermediateDecimalMergeSerializedString(inputCol, scale, stream); +} + +std::unique_ptr reduceFinalDecimalSumFromSerializedColumn( + cudf::column_view inputCol, + TypePtr const& outputType, + rmm::cuda_stream_view stream) { + validateIntermediateColumnType(inputCol); + auto scale = getDecimalPrecisionScale(*outputType).second; + auto decodedSum = finalDecimalSumDecodeColumn(inputCol, scale, stream); + return singleOrRawDecimalSumWithCast( + decodedSum->view(), outputType, stream); +} + +std::unique_ptr reduceFinalDecimalAvgFromSerializedColumn( + cudf::column_view inputCol, + TypePtr const& outputType, + TypePtr const& resultType, + rmm::cuda_stream_view stream) { + validateIntermediateColumnType(inputCol); + auto scale = getDecimalPrecisionScale(*outputType).second; + return finalDecimalAvgFromSerializedString( + inputCol, scale, resultType, stream); +} + struct ReduceDecimalSumAggregator : ReduceAggregator { ReduceDecimalSumAggregator( core::AggregationNode::Step step, @@ -400,22 +434,12 @@ struct ReduceDecimalSumAggregator : ReduceAggregator { return singleOrRawDecimalSumWithCast(inputCol, outputType, stream); case core::AggregationNode::Step::kPartial: return partialDecimalSumCountToSerializedString(inputCol, stream); - case core::AggregationNode::Step::kIntermediate: { - VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); - auto scale = outputType->isDecimal() - ? getDecimalPrecisionScale(*outputType).second - : 0; - return intermediateDecimalMergeSerializedString( - inputCol, scale, stream); - } - case core::AggregationNode::Step::kFinal: { - VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); - auto scale = getDecimalPrecisionScale(*outputType).second; - auto decodedSum = - finalDecimalSumDecodeColumn(inputCol, scale, stream); - return singleOrRawDecimalSumWithCast( - decodedSum->view(), outputType, stream); - } + case core::AggregationNode::Step::kIntermediate: + return reduceIntermediateDecimalFromSerializedColumn( + inputCol, outputType, stream); + case core::AggregationNode::Step::kFinal: + return reduceFinalDecimalSumFromSerializedColumn( + inputCol, outputType, stream); default: VELOX_NYI("Unsupported aggregation step for decimal sum reduce"); } @@ -442,20 +466,12 @@ struct ReduceDecimalAvgAggregator : ReduceAggregator { inputCol, resultType, stream); case core::AggregationNode::Step::kPartial: return partialDecimalSumCountToSerializedString(inputCol, stream); - case core::AggregationNode::Step::kIntermediate: { - VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); - auto scale = outputType->isDecimal() - ? getDecimalPrecisionScale(*outputType).second - : 0; - return intermediateDecimalMergeSerializedString( - inputCol, scale, stream); - } - case core::AggregationNode::Step::kFinal: { - VELOX_CHECK(inputCol.type().id() == cudf::type_id::STRING); - auto scale = getDecimalPrecisionScale(*outputType).second; - return finalDecimalAvgFromSerializedString( - inputCol, scale, resultType, stream); - } + case core::AggregationNode::Step::kIntermediate: + return reduceIntermediateDecimalFromSerializedColumn( + inputCol, outputType, stream); + case core::AggregationNode::Step::kFinal: + return reduceFinalDecimalAvgFromSerializedColumn( + inputCol, outputType, resultType, stream); default: VELOX_NYI("Unsupported aggregation step for decimal avg reduce"); } diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp index f71d4e9e752..acfe246ee07 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -16,6 +16,7 @@ #include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" +#include "velox/common/base/Exceptions.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" @@ -24,6 +25,12 @@ namespace facebook::velox::cudf_velox { +void validateIntermediateColumnType(cudf::column_view const& column) { + VELOX_CHECK( + column.type().id() == cudf::type_id::STRING, + "Expected serialized decimal aggregation state: Velox VARBINARY represented as cuDF STRING"); +} + std::unique_ptr castCountColumnToInt64( std::unique_ptr count, rmm::cuda_stream_view stream) { diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.h b/velox/experimental/cudf/exec/DecimalAggregationCommon.h index 974c19a0c3d..1d10d8850d1 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.h +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.h @@ -18,6 +18,7 @@ #include "velox/type/Type.h" #include +#include #include @@ -25,6 +26,9 @@ namespace facebook::velox::cudf_velox { +/// Checks that the column is STRING-encoded serialized decimal aggregation state. +void validateIntermediateColumnType(cudf::column_view const& column); + std::unique_ptr castCountColumnToInt64( std::unique_ptr count, rmm::cuda_stream_view stream); From 57c6593eaf7f3e1dea88210d37d596061c8337f3 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 11 May 2026 13:45:06 -0700 Subject: [PATCH 15/64] Add comment about held state, per @mattgara, and format (cherry picked from commit c6127b0c3849da174d989a25054295e638739cb9) --- velox/experimental/cudf/exec/CudfGroupby.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index f91b702e1e3..6464a858d7a 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -92,6 +92,12 @@ DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Sum, sum, SUM) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Min, min, MIN) DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Max, max, MAX) +// Decimal SUM and AVG aggregators are separate implementations, as they need to +// handle the VARBINARY encoded intermediate state for streaming aggregation. +// Unlike other aggregators, these classes hold state (the decoded intermediate +// sum and count columns and associated indices) in order to guarantee a +// lifetime constraint between aggregation steps. + void addDecimalSumCountRequestsAfterDecode( cudf::column_view encodedColumn, int32_t scale, @@ -229,13 +235,7 @@ struct GroupbyDecimalSumAggregator : GroupbyAggregator { decodedCount_); } else if (step == core::AggregationNode::Step::kFinal) { addDecimalFinalSumOnlyRequest( - tbl, - inputIndex, - resultType, - requests, - stream, - sumIdx_, - decodedSum_); + tbl, inputIndex, resultType, requests, stream, sumIdx_, decodedSum_); } else { addDecimalRawPartialSingleSumRequest( tbl, From 37d434d5019b01f4b3a00a2e6d89c4baa5fbc58a Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 11 May 2026 15:36:12 -0700 Subject: [PATCH 16/64] Rewrite agg kernels to use cub::DeviceFor, per @devavret (cherry picked from commit 4ce2f9469f5452bc457b01489c318acd0ff67c7c) --- .../cudf/exec/DecimalAggregationKernels.cu | 249 +++++++++++------- 1 file changed, 152 insertions(+), 97 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cu b/velox/experimental/cudf/exec/DecimalAggregationKernels.cu index 27d74e36b15..cc21f386eea 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cu @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -58,73 +59,132 @@ splitToWords(__int128_t value, int64_t& upper, uint64_t& lower) { } template -__global__ void fillOffsetsKernel(OffsetT* offsets, int32_t numRows) { - int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx <= numRows) { +struct FillOffsetsFunctor { + OffsetT* offsets; + + __device__ void operator()(int32_t idx) const { int64_t offset = static_cast(idx) * kStateSize; offsets[idx] = static_cast(offset); } +}; + +template +struct PackStateFunctor { + const SumT* sums; + const int64_t* counts; + const OffsetT* offsets; + uint8_t* chars; + + __device__ void operator()(int32_t idx) const { + int64_t offset = static_cast(offsets[idx]); + auto* state = reinterpret_cast(chars + offset); + int64_t upper; + uint64_t lower; + splitToWords(sums[idx], upper, lower); + state->count = counts[idx]; + state->overflow = 0; + state->lower = lower; + state->upper = upper; + } +}; + +template +struct UnpackStateFunctor { + const OffsetT* offsets; + const uint8_t* chars; + __int128_t* sums; + int64_t* counts; + + __device__ void operator()(int32_t idx) const { + int64_t offset = static_cast(offsets[idx]); + auto* state = reinterpret_cast(chars + offset); + counts[idx] = state->count; + sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; + } +}; + +template +struct AvgRoundFunctor { + const SumT* sums; + const int64_t* counts; + SumT* out; + + __device__ void operator()(int32_t idx) const { + auto count = counts[idx]; + if (count == 0) { + out[idx] = SumT{0}; + return; + } + auto sum = sums[idx]; + SumT absSum = sum < 0 ? -sum : sum; + SumT half = static_cast(count / 2); + SumT rounded = (absSum + half) / static_cast(count); + out[idx] = sum < 0 ? -rounded : rounded; + } +}; + +template +void launchFillOffsets( + OffsetT* offsets, + int32_t numRows, + rmm::cuda_stream_view stream) { + FillOffsetsFunctor op{offsets}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), + numRows + 1, + op, + stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); } template -__global__ void packStateKernel( +void launchPackState( const SumT* sums, const int64_t* counts, const OffsetT* offsets, uint8_t* chars, - int32_t numRows) { - int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx >= numRows) { - return; - } - int64_t offset = static_cast(offsets[idx]); - auto* state = reinterpret_cast(chars + offset); - int64_t upper; - uint64_t lower; - splitToWords(sums[idx], upper, lower); - state->count = counts[idx]; - state->overflow = 0; - state->lower = lower; - state->upper = upper; + int32_t numRows, + rmm::cuda_stream_view stream) { + PackStateFunctor op{sums, counts, offsets, chars}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), + numRows, + op, + stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); } template -__global__ void unpackStateKernel( +void launchUnpackState( const OffsetT* offsets, const uint8_t* chars, __int128_t* sums, int64_t* counts, - int32_t numRows) { - int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx >= numRows) { - return; - } - int64_t offset = static_cast(offsets[idx]); - auto* state = reinterpret_cast(chars + offset); - counts[idx] = state->count; - sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; + int32_t numRows, + rmm::cuda_stream_view stream) { + UnpackStateFunctor op{offsets, chars, sums, counts}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), + numRows, + op, + stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); } template -__global__ void avgRoundKernel( +void launchAvgRound( const SumT* sums, const int64_t* counts, SumT* out, - int32_t numRows) { - int32_t idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx >= numRows) { - return; - } - auto count = counts[idx]; - if (count == 0) { - out[idx] = SumT{0}; - return; - } - auto sum = sums[idx]; - SumT absSum = sum < 0 ? -sum : sum; - SumT half = static_cast(count / 2); - SumT rounded = (absSum + half) / static_cast(count); - out[idx] = sum < 0 ? -rounded : rounded; + int32_t numRows, + rmm::cuda_stream_view stream) { + AvgRoundFunctor op{sums, counts, out}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), + numRows, + op, + stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); } struct StateValidPredicate { @@ -221,29 +281,29 @@ DecimalSumStateColumns deserializeDecimalSumStateWithCount( auto countView = countCol->mutable_view(); if (numRows > 0) { - int32_t blockSize = 256; - int32_t gridSize = (numRows + blockSize - 1) / blockSize; + auto const rowCount = static_cast(numRows); if (offsetsType == cudf::type_id::INT64) { auto offsetsCol = offsetsView.data(); - unpackStateKernel<<>>( + launchUnpackState( offsetsCol, charsPtr, sumView.data<__int128_t>(), countView.data(), - numRows); + rowCount, + stream); } else { CUDF_EXPECTS( offsetsType == cudf::type_id::INT32, "Decimal sum state requires INT32 or INT64 offsets"); auto offsetsCol = offsetsView.data(); - unpackStateKernel<<>>( + launchUnpackState( offsetsCol, charsPtr, sumView.data<__int128_t>(), countView.data(), - numRows); + rowCount, + stream); } - CUDF_CUDA_TRY(cudaGetLastError()); } if (stateCol.nullable()) { @@ -286,6 +346,8 @@ std::unique_ptr serializeDecimalSumState( numRows <= std::numeric_limits::max(), "Too many rows to serialize decimal sum state"); + auto const rowCount = static_cast(numRows); + auto const charsBytes = static_cast(numRows) * kStateSize; auto const threshold = cudf::strings::get_offset64_threshold(); auto const useLargeOffsets = charsBytes >= threshold; @@ -306,68 +368,61 @@ std::unique_ptr serializeDecimalSumState( rmm::device_buffer charsBuf( static_cast(numRows) * kStateSize, stream); - int32_t blockSize = 256; - int32_t offsetGridSize = (numRows + 1 + blockSize - 1) / blockSize; if (useLargeOffsets) { - fillOffsetsKernel - <<>>( - offsetsView.data(), numRows); + launchFillOffsets( + offsetsView.data(), rowCount, stream); } else { - fillOffsetsKernel - <<>>( - offsetsView.data(), numRows); + launchFillOffsets( + offsetsView.data(), rowCount, stream); } - CUDF_CUDA_TRY(cudaGetLastError()); if (numRows > 0) { - int32_t gridSize = (numRows + blockSize - 1) / blockSize; auto charsPtr = reinterpret_cast(charsBuf.data()); if (useLargeOffsets) { auto offsetsPtr = offsetsView.data(); if (sumCol.type().id() == cudf::type_id::DECIMAL64) { - packStateKernel - <<>>( - sumCol.data(), - countCol.data(), - offsetsPtr, - charsPtr, - numRows); + launchPackState( + sumCol.data(), + countCol.data(), + offsetsPtr, + charsPtr, + rowCount, + stream); } else { CUDF_EXPECTS( sumCol.type().id() == cudf::type_id::DECIMAL128, "Unsupported decimal sum column type"); - packStateKernel<__int128_t, int64_t> - <<>>( - sumCol.data<__int128_t>(), - countCol.data(), - offsetsPtr, - charsPtr, - numRows); + launchPackState<__int128_t, int64_t>( + sumCol.data<__int128_t>(), + countCol.data(), + offsetsPtr, + charsPtr, + rowCount, + stream); } } else { auto offsetsPtr = offsetsView.data(); if (sumCol.type().id() == cudf::type_id::DECIMAL64) { - packStateKernel - <<>>( - sumCol.data(), - countCol.data(), - offsetsPtr, - charsPtr, - numRows); + launchPackState( + sumCol.data(), + countCol.data(), + offsetsPtr, + charsPtr, + rowCount, + stream); } else { CUDF_EXPECTS( sumCol.type().id() == cudf::type_id::DECIMAL128, "Unsupported decimal sum column type"); - packStateKernel<__int128_t, int32_t> - <<>>( - sumCol.data<__int128_t>(), - countCol.data(), - offsetsPtr, - charsPtr, - numRows); + launchPackState<__int128_t, int32_t>( + sumCol.data<__int128_t>(), + countCol.data(), + offsetsPtr, + charsPtr, + rowCount, + stream); } } - CUDF_CUDA_TRY(cudaGetLastError()); } auto [nullMask, nullCount] = @@ -401,22 +456,22 @@ std::unique_ptr computeDecimalAverage( sumCol.type(), numRows, cudf::mask_state::UNALLOCATED, stream); if (numRows > 0) { - int32_t blockSize = 256; - int32_t gridSize = (numRows + blockSize - 1) / blockSize; + auto const rowCount = static_cast(numRows); if (sumCol.type().id() == cudf::type_id::DECIMAL64) { - avgRoundKernel<<>>( + launchAvgRound( sumCol.data(), countCol.data(), out->mutable_view().data(), - numRows); + rowCount, + stream); } else { - avgRoundKernel<<>>( + launchAvgRound<__int128_t>( sumCol.data<__int128_t>(), countCol.data(), out->mutable_view().data<__int128_t>(), - numRows); + rowCount, + stream); } - CUDF_CUDA_TRY(cudaGetLastError()); } auto [nullMask, nullCount] = From 0f00a1249aae811190a3a5c1bc27b021cb9601a4 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Mon, 11 May 2026 15:51:49 -0700 Subject: [PATCH 17/64] Format --- velox/experimental/cudf/exec/CudfReduce.cpp | 25 ++++++--------- .../cudf/exec/DecimalAggregationCommon.cpp | 12 +++---- .../cudf/exec/DecimalAggregationCommon.h | 3 +- .../cudf/exec/DecimalAggregationKernels.cu | 31 ++++++------------- 4 files changed, 24 insertions(+), 47 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 0cbe542f1bd..3482bdfbe8b 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -46,10 +46,10 @@ using facebook::velox::cudf_velox::CountInputKind; using facebook::velox::cudf_velox::finalizeDecimalAverage; using facebook::velox::cudf_velox::get_output_mr; using facebook::velox::cudf_velox::get_temp_mr; -using facebook::velox::cudf_velox::serializeDecimalPartialOrIntermediateState; -using facebook::velox::cudf_velox::validateIntermediateColumnType; using facebook::velox::cudf_velox::ReduceAggregator; using facebook::velox::cudf_velox::ResolvedAggregateInfo; +using facebook::velox::cudf_velox::serializeDecimalPartialOrIntermediateState; +using facebook::velox::cudf_velox::validateIntermediateColumnType; #define DEFINE_SIMPLE_REDUCE_AGGREGATOR(Name, name) \ struct Reduce##Name##Aggregator : ReduceAggregator { \ @@ -255,8 +255,7 @@ struct ReduceMeanAggregator : ReduceAggregator { std::unique_ptr partialDecimalSumCountToSerializedString( cudf::column_view inputCol, rmm::cuda_stream_view stream) { - auto const sumAgg = - cudf::make_sum_aggregation(); + auto const sumAgg = cudf::make_sum_aggregation(); auto sumScalar = cudf::reduce(inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); auto countAgg = cudf::make_count_aggregation( @@ -279,8 +278,7 @@ std::unique_ptr intermediateDecimalMergeSerializedString( cudf::column_view inputCol, int32_t scale, rmm::cuda_stream_view stream) { - auto const sumAgg = - cudf::make_sum_aggregation(); + auto const sumAgg = cudf::make_sum_aggregation(); auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( inputCol, scale, stream, get_output_mr()); auto sumScalar = cudf::reduce( @@ -308,8 +306,7 @@ std::unique_ptr finalDecimalAvgFromSerializedString( int32_t scale, TypePtr const& resultType, rmm::cuda_stream_view stream) { - auto const sumAgg = - cudf::make_sum_aggregation(); + auto const sumAgg = cudf::make_sum_aggregation(); auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( inputCol, scale, stream, get_output_mr()); auto sumScalar = cudf::reduce( @@ -344,8 +341,7 @@ std::unique_ptr singleDecimalAvgFromRawColumn( cudf::column_view inputCol, TypePtr const& resultType, rmm::cuda_stream_view stream) { - auto const sumAgg = - cudf::make_sum_aggregation(); + auto const sumAgg = cudf::make_sum_aggregation(); auto sumScalar = cudf::reduce(inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); auto countAgg = cudf::make_count_aggregation( @@ -368,8 +364,7 @@ std::unique_ptr singleOrRawDecimalSumWithCast( cudf::column_view inputCol, TypePtr const& outputType, rmm::cuda_stream_view stream) { - auto const sumAgg = - cudf::make_sum_aggregation(); + auto const sumAgg = cudf::make_sum_aggregation(); auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); std::unique_ptr castedInput; if (outputType->isDecimal() && inputCol.type() != cudfOutType) { @@ -400,8 +395,7 @@ std::unique_ptr reduceFinalDecimalSumFromSerializedColumn( validateIntermediateColumnType(inputCol); auto scale = getDecimalPrecisionScale(*outputType).second; auto decodedSum = finalDecimalSumDecodeColumn(inputCol, scale, stream); - return singleOrRawDecimalSumWithCast( - decodedSum->view(), outputType, stream); + return singleOrRawDecimalSumWithCast(decodedSum->view(), outputType, stream); } std::unique_ptr reduceFinalDecimalAvgFromSerializedColumn( @@ -462,8 +456,7 @@ struct ReduceDecimalAvgAggregator : ReduceAggregator { cudf::column_view inputCol = input.column(inputIndex); switch (step) { case core::AggregationNode::Step::kSingle: - return singleDecimalAvgFromRawColumn( - inputCol, resultType, stream); + return singleDecimalAvgFromRawColumn(inputCol, resultType, stream); case core::AggregationNode::Step::kPartial: return partialDecimalSumCountToSerializedString(inputCol, stream); case core::AggregationNode::Step::kIntermediate: diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp index acfe246ee07..95d5c7a88fc 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -15,12 +15,12 @@ */ #include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" - -#include "velox/common/base/Exceptions.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" +#include "velox/common/base/Exceptions.h" + #include namespace facebook::velox::cudf_velox { @@ -36,10 +36,7 @@ std::unique_ptr castCountColumnToInt64( rmm::cuda_stream_view stream) { if (count->type().id() != cudf::type_id::INT64) { count = cudf::cast( - *count, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_output_mr()); + *count, cudf::data_type{cudf::type_id::INT64}, stream, get_output_mr()); } return count; } @@ -63,8 +60,7 @@ std::unique_ptr finalizeDecimalAverage( sum->view(), count->view(), stream, get_output_mr()); auto const cudfOutType = veloxToCudfDataType(resultType); if (avgCol->type() != cudfOutType) { - avgCol = cudf::cast( - avgCol->view(), cudfOutType, stream, get_output_mr()); + avgCol = cudf::cast(avgCol->view(), cudfOutType, stream, get_output_mr()); } return avgCol; } diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.h b/velox/experimental/cudf/exec/DecimalAggregationCommon.h index 1d10d8850d1..35e8b9e4b12 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.h +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.h @@ -26,7 +26,8 @@ namespace facebook::velox::cudf_velox { -/// Checks that the column is STRING-encoded serialized decimal aggregation state. +/// Checks that the column is STRING-encoded serialized decimal aggregation +/// state. void validateIntermediateColumnType(cudf::column_view const& column); std::unique_ptr castCountColumnToInt64( diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cu b/velox/experimental/cudf/exec/DecimalAggregationKernels.cu index cc21f386eea..514ee436a10 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cu @@ -24,8 +24,8 @@ #include #include -#include #include +#include #include #include @@ -97,7 +97,8 @@ struct UnpackStateFunctor { __device__ void operator()(int32_t idx) const { int64_t offset = static_cast(offsets[idx]); - auto* state = reinterpret_cast(chars + offset); + auto* state = + reinterpret_cast(chars + offset); counts[idx] = state->count; sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; } @@ -130,10 +131,7 @@ void launchFillOffsets( rmm::cuda_stream_view stream) { FillOffsetsFunctor op{offsets}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), - numRows + 1, - op, - stream.value()); + thrust::counting_iterator(0), numRows + 1, op, stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } @@ -147,10 +145,7 @@ void launchPackState( rmm::cuda_stream_view stream) { PackStateFunctor op{sums, counts, offsets, chars}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), - numRows, - op, - stream.value()); + thrust::counting_iterator(0), numRows, op, stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } @@ -164,10 +159,7 @@ void launchUnpackState( rmm::cuda_stream_view stream) { UnpackStateFunctor op{offsets, chars, sums, counts}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), - numRows, - op, - stream.value()); + thrust::counting_iterator(0), numRows, op, stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } @@ -180,10 +172,7 @@ void launchAvgRound( rmm::cuda_stream_view stream) { AvgRoundFunctor op{sums, counts, out}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), - numRows, - op, - stream.value()); + thrust::counting_iterator(0), numRows, op, stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } @@ -369,11 +358,9 @@ std::unique_ptr serializeDecimalSumState( static_cast(numRows) * kStateSize, stream); if (useLargeOffsets) { - launchFillOffsets( - offsetsView.data(), rowCount, stream); + launchFillOffsets(offsetsView.data(), rowCount, stream); } else { - launchFillOffsets( - offsetsView.data(), rowCount, stream); + launchFillOffsets(offsetsView.data(), rowCount, stream); } if (numRows > 0) { From 62a578149723f1a4f60d066a0d9e28693a1cc09f Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 12 May 2026 13:25:51 -0700 Subject: [PATCH 18/64] Refactor to keep CUDA/cub/Thrust code in .cu file and move everything else to a regular .cpp file, using VELOX_CHECK instead of CUDF_EXPECTS --- velox/experimental/cudf/exec/CMakeLists.txt | 3 +- .../cudf/exec/DecimalAggregationKernels.cpp | 261 ++++++++++ .../cudf/exec/DecimalAggregationKernels.cu | 474 ------------------ .../cudf/exec/DecimalAggregationKernelsGpu.cu | 328 ++++++++++++ .../cudf/exec/DecimalAggregationKernelsGpu.h | 72 +++ 5 files changed, 663 insertions(+), 475 deletions(-) create mode 100644 velox/experimental/cudf/exec/DecimalAggregationKernels.cpp delete mode 100644 velox/experimental/cudf/exec/DecimalAggregationKernels.cu create mode 100644 velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu create mode 100644 velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h diff --git a/velox/experimental/cudf/exec/CMakeLists.txt b/velox/experimental/cudf/exec/CMakeLists.txt index a0a68811d37..9afcd51d2e9 100644 --- a/velox/experimental/cudf/exec/CMakeLists.txt +++ b/velox/experimental/cudf/exec/CMakeLists.txt @@ -32,7 +32,8 @@ add_library( CudfTopN.cpp DebugUtil.cpp DecimalAggregationCommon.cpp - DecimalAggregationKernels.cu + DecimalAggregationKernels.cpp + DecimalAggregationKernelsGpu.cu GpuResources.cpp OperatorAdapters.cpp PrestoAggregateFunctions.cpp diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp new file mode 100644 index 00000000000..724903f3962 --- /dev/null +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/experimental/cudf/exec/DecimalAggregationKernels.h" +#include "velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h" + +#include "velox/common/base/Exceptions.h" + +#include +#include +#include +#include + +#include + +namespace facebook::velox::cudf_velox { + +DecimalSumStateColumns deserializeDecimalSumStateWithCount( + const cudf::column_view& stateCol, + int32_t scale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + VELOX_CHECK( + stateCol.type().id() == cudf::type_id::STRING, + "Decimal sum state requires STRING/VARBINARY column"); + auto numRows = stateCol.size(); + if (numRows == 0) { + DecimalSumStateColumns empty; + empty.sum = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::DECIMAL128, -scale}, + 0, + cudf::mask_state::UNALLOCATED, + stream); + empty.count = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::INT64}, + 0, + cudf::mask_state::UNALLOCATED, + stream); + return empty; + } + + // For fully-null state columns there is nothing to deserialize. Avoid + // launching unpack kernels over string payload buffers that may be empty. + if (stateCol.nullable() && stateCol.null_count() == numRows) { + DecimalSumStateColumns allNull; + allNull.sum = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::DECIMAL128, -scale}, + numRows, + cudf::mask_state::ALL_NULL, + stream); + allNull.count = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::INT64}, + numRows, + cudf::mask_state::ALL_NULL, + stream); + return allNull; + } + + cudf::strings_column_view strings(stateCol); + numRows = strings.size(); + + auto offsetsView = strings.offsets(); + auto offsetsType = offsetsView.type().id(); + auto charsPtr = reinterpret_cast(strings.chars_begin(stream)); + + auto sumCol = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::DECIMAL128, -scale}, + numRows, + cudf::mask_state::UNALLOCATED, + stream); + auto countCol = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::INT64}, + numRows, + cudf::mask_state::UNALLOCATED, + stream); + + auto sumView = sumCol->mutable_view(); + auto countView = countCol->mutable_view(); + + if (numRows > 0) { + auto const rowCount = static_cast(numRows); + const bool offsets64 = (offsetsType == cudf::type_id::INT64); + VELOX_CHECK( + offsets64 || offsetsType == cudf::type_id::INT32, + "Decimal sum state requires INT32 or INT64 offsets"); + detail::unpackDecimalSumState( + offsets64, + offsets64 ? static_cast(offsetsView.data()) + : static_cast(offsetsView.data()), + charsPtr, + sumView.data<__int128_t>(), + countView.data(), + rowCount, + stream); + } + + if (stateCol.nullable()) { + auto nullMask = cudf::copy_bitmask(stateCol, stream, mr); + auto nullCount = stateCol.null_count(); + sumCol->set_null_mask(std::move(nullMask), nullCount); + auto countMask = cudf::copy_bitmask(stateCol, stream, mr); + countCol->set_null_mask(std::move(countMask), nullCount); + } + + DecimalSumStateColumns result; + result.sum = std::move(sumCol); + result.count = std::move(countCol); + return result; +} + +std::unique_ptr deserializeDecimalSumState( + const cudf::column_view& stateCol, + int32_t scale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + auto decoded = + deserializeDecimalSumStateWithCount(stateCol, scale, stream, mr); + return std::move(decoded.sum); +} + +std::unique_ptr serializeDecimalSumState( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + VELOX_CHECK( + countCol.type().id() == cudf::type_id::INT64, + "Decimal sum state requires INT64 count column"); + auto numRows = sumCol.size(); + VELOX_CHECK_EQ( + numRows, + countCol.size(), + "Decimal sum state requires sum and count to be same size"); + VELOX_CHECK_LE( + numRows, + static_cast(std::numeric_limits::max()), + "Too many rows to serialize decimal sum state"); + + auto const rowCount = static_cast(numRows); + + auto const charsBytes = + static_cast(numRows) * detail::kDecimalSumStateSize; + auto const threshold = cudf::strings::get_offset64_threshold(); + auto const useLargeOffsets = charsBytes >= threshold; + // Previously this guard threw std::overflow_error; Velox uses + // VeloxRuntimeError for this guard. + VELOX_CHECK( + !useLargeOffsets || cudf::strings::is_large_strings_enabled(), + "Size of output exceeds the column size limit"); + + auto const offsetsType = + useLargeOffsets ? cudf::type_id::INT64 : cudf::type_id::INT32; + auto offsetsCol = cudf::make_fixed_width_column( + cudf::data_type{offsetsType}, + numRows + 1, + cudf::mask_state::UNALLOCATED, + stream); + auto offsetsView = offsetsCol->mutable_view(); + + rmm::device_buffer charsBuf( + static_cast(numRows) * detail::kDecimalSumStateSize, stream); + + detail::fillOffsetsForDecimalSumState( + useLargeOffsets, + useLargeOffsets ? static_cast(offsetsView.data()) + : static_cast(offsetsView.data()), + rowCount, + stream); + + if (numRows > 0) { + auto charsPtr = reinterpret_cast(charsBuf.data()); + const void* offsetsPtr = useLargeOffsets + ? static_cast(offsetsView.data()) + : static_cast(offsetsView.data()); + const auto sumType = sumCol.type().id(); + VELOX_CHECK( + sumType == cudf::type_id::DECIMAL64 || + sumType == cudf::type_id::DECIMAL128, + "Unsupported decimal sum column type"); + const void* sumPtr = sumType == cudf::type_id::DECIMAL64 + ? static_cast(sumCol.data()) + : static_cast(sumCol.data<__int128_t>()); + detail::packDecimalSumState( + sumType, + useLargeOffsets, + sumPtr, + countCol.data(), + offsetsPtr, + charsPtr, + rowCount, + stream); + } + + auto [nullMask, nullCount] = + detail::buildStateValidityMask(sumCol, countCol, stream, mr); + return cudf::make_strings_column( + static_cast(numRows), + std::move(offsetsCol), + std::move(charsBuf), + nullCount, + std::move(nullMask)); +} + +std::unique_ptr computeDecimalAverage( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + VELOX_CHECK( + countCol.type().id() == cudf::type_id::INT64, + "Decimal average requires INT64 count column"); + VELOX_CHECK( + sumCol.type().id() == cudf::type_id::DECIMAL64 || + sumCol.type().id() == cudf::type_id::DECIMAL128, + "Decimal average requires DECIMAL64 or DECIMAL128 sum column"); + VELOX_CHECK_EQ( + sumCol.size(), + countCol.size(), + "Decimal average requires sum and count to be same size"); + + auto numRows = sumCol.size(); + auto out = cudf::make_fixed_width_column( + sumCol.type(), numRows, cudf::mask_state::UNALLOCATED, stream); + + if (numRows > 0) { + auto const rowCount = static_cast(numRows); + const auto sumType = sumCol.type().id(); + const void* sumsPtr = sumType == cudf::type_id::DECIMAL64 + ? static_cast(sumCol.data()) + : static_cast(sumCol.data<__int128_t>()); + void* outPtr = sumType == cudf::type_id::DECIMAL64 + ? static_cast(out->mutable_view().data()) + : static_cast(out->mutable_view().data<__int128_t>()); + detail::averageRoundDecimalSum( + sumType, sumsPtr, countCol.data(), outPtr, rowCount, stream); + } + + auto [nullMask, nullCount] = + detail::buildStateValidityMask(sumCol, countCol, stream, mr); + if (nullCount > 0) { + out->set_null_mask(std::move(nullMask), nullCount); + } else if (nullMask.size() > 0) { + out->set_null_mask(std::move(nullMask), 0); + } + return out; +} + +} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cu b/velox/experimental/cudf/exec/DecimalAggregationKernels.cu deleted file mode 100644 index 514ee436a10..00000000000 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cu +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * 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 "velox/experimental/cudf/exec/DecimalAggregationKernels.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -namespace facebook::velox::cudf_velox { -namespace { - -constexpr int32_t kStateSize = 32; - -struct DecimalSumStateDevice { - int64_t count; - int64_t overflow; - uint64_t lower; - int64_t upper; -}; - -static_assert(sizeof(DecimalSumStateDevice) == kStateSize); - -__device__ __forceinline__ void -splitToWords(int64_t value, int64_t& upper, uint64_t& lower) { - lower = static_cast(value); - upper = value < 0 ? -1 : 0; -} - -__device__ __forceinline__ void -splitToWords(__int128_t value, int64_t& upper, uint64_t& lower) { - lower = static_cast(value); - upper = static_cast(value >> 64); -} - -template -struct FillOffsetsFunctor { - OffsetT* offsets; - - __device__ void operator()(int32_t idx) const { - int64_t offset = static_cast(idx) * kStateSize; - offsets[idx] = static_cast(offset); - } -}; - -template -struct PackStateFunctor { - const SumT* sums; - const int64_t* counts; - const OffsetT* offsets; - uint8_t* chars; - - __device__ void operator()(int32_t idx) const { - int64_t offset = static_cast(offsets[idx]); - auto* state = reinterpret_cast(chars + offset); - int64_t upper; - uint64_t lower; - splitToWords(sums[idx], upper, lower); - state->count = counts[idx]; - state->overflow = 0; - state->lower = lower; - state->upper = upper; - } -}; - -template -struct UnpackStateFunctor { - const OffsetT* offsets; - const uint8_t* chars; - __int128_t* sums; - int64_t* counts; - - __device__ void operator()(int32_t idx) const { - int64_t offset = static_cast(offsets[idx]); - auto* state = - reinterpret_cast(chars + offset); - counts[idx] = state->count; - sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; - } -}; - -template -struct AvgRoundFunctor { - const SumT* sums; - const int64_t* counts; - SumT* out; - - __device__ void operator()(int32_t idx) const { - auto count = counts[idx]; - if (count == 0) { - out[idx] = SumT{0}; - return; - } - auto sum = sums[idx]; - SumT absSum = sum < 0 ? -sum : sum; - SumT half = static_cast(count / 2); - SumT rounded = (absSum + half) / static_cast(count); - out[idx] = sum < 0 ? -rounded : rounded; - } -}; - -template -void launchFillOffsets( - OffsetT* offsets, - int32_t numRows, - rmm::cuda_stream_view stream) { - FillOffsetsFunctor op{offsets}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows + 1, op, stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchPackState( - const SumT* sums, - const int64_t* counts, - const OffsetT* offsets, - uint8_t* chars, - int32_t numRows, - rmm::cuda_stream_view stream) { - PackStateFunctor op{sums, counts, offsets, chars}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows, op, stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchUnpackState( - const OffsetT* offsets, - const uint8_t* chars, - __int128_t* sums, - int64_t* counts, - int32_t numRows, - rmm::cuda_stream_view stream) { - UnpackStateFunctor op{offsets, chars, sums, counts}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows, op, stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchAvgRound( - const SumT* sums, - const int64_t* counts, - SumT* out, - int32_t numRows, - rmm::cuda_stream_view stream) { - AvgRoundFunctor op{sums, counts, out}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows, op, stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -struct StateValidPredicate { - cudf::column_device_view sum; - cudf::column_device_view count; - - __device__ bool operator()(cudf::size_type idx) const { - if (sum.is_null(idx) || count.is_null(idx)) { - return false; - } - return count.element(idx) != 0; - } -}; - -std::pair buildStateValidityMask( - const cudf::column_view& sumCol, - const cudf::column_view& countCol, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - auto numRows = sumCol.size(); - if (numRows == 0) { - return {rmm::device_buffer{}, 0}; - } - auto sumDeviceView = cudf::column_device_view::create(sumCol, stream); - auto countDeviceView = cudf::column_device_view::create(countCol, stream); - StateValidPredicate pred{*sumDeviceView, *countDeviceView}; - auto begin = thrust::make_counting_iterator(0); - auto end = begin + numRows; - return cudf::detail::valid_if(begin, end, pred, stream, mr); -} - -} // namespace - -DecimalSumStateColumns deserializeDecimalSumStateWithCount( - const cudf::column_view& stateCol, - int32_t scale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - CUDF_EXPECTS( - stateCol.type().id() == cudf::type_id::STRING, - "Decimal sum state requires STRING/VARBINARY column"); - auto numRows = stateCol.size(); - if (numRows == 0) { - DecimalSumStateColumns empty; - empty.sum = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_id::DECIMAL128, -scale}, - 0, - cudf::mask_state::UNALLOCATED, - stream); - empty.count = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_id::INT64}, - 0, - cudf::mask_state::UNALLOCATED, - stream); - return empty; - } - - // For fully-null state columns there is nothing to deserialize. Avoid - // launching unpack kernels over string payload buffers that may be empty. - if (stateCol.nullable() && stateCol.null_count() == numRows) { - DecimalSumStateColumns allNull; - allNull.sum = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_id::DECIMAL128, -scale}, - numRows, - cudf::mask_state::ALL_NULL, - stream); - allNull.count = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_id::INT64}, - numRows, - cudf::mask_state::ALL_NULL, - stream); - return allNull; - } - - cudf::strings_column_view strings(stateCol); - numRows = strings.size(); - - auto offsetsView = strings.offsets(); - auto offsetsType = offsetsView.type().id(); - auto charsPtr = reinterpret_cast(strings.chars_begin(stream)); - - auto sumCol = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_id::DECIMAL128, -scale}, - numRows, - cudf::mask_state::UNALLOCATED, - stream); - auto countCol = cudf::make_fixed_width_column( - cudf::data_type{cudf::type_id::INT64}, - numRows, - cudf::mask_state::UNALLOCATED, - stream); - - auto sumView = sumCol->mutable_view(); - auto countView = countCol->mutable_view(); - - if (numRows > 0) { - auto const rowCount = static_cast(numRows); - if (offsetsType == cudf::type_id::INT64) { - auto offsetsCol = offsetsView.data(); - launchUnpackState( - offsetsCol, - charsPtr, - sumView.data<__int128_t>(), - countView.data(), - rowCount, - stream); - } else { - CUDF_EXPECTS( - offsetsType == cudf::type_id::INT32, - "Decimal sum state requires INT32 or INT64 offsets"); - auto offsetsCol = offsetsView.data(); - launchUnpackState( - offsetsCol, - charsPtr, - sumView.data<__int128_t>(), - countView.data(), - rowCount, - stream); - } - } - - if (stateCol.nullable()) { - auto nullMask = cudf::copy_bitmask(stateCol, stream, mr); - auto nullCount = stateCol.null_count(); - sumCol->set_null_mask(std::move(nullMask), nullCount); - auto countMask = cudf::copy_bitmask(stateCol, stream, mr); - countCol->set_null_mask(std::move(countMask), nullCount); - } - - DecimalSumStateColumns result; - result.sum = std::move(sumCol); - result.count = std::move(countCol); - return result; -} - -std::unique_ptr deserializeDecimalSumState( - const cudf::column_view& stateCol, - int32_t scale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - auto decoded = - deserializeDecimalSumStateWithCount(stateCol, scale, stream, mr); - return std::move(decoded.sum); -} - -std::unique_ptr serializeDecimalSumState( - const cudf::column_view& sumCol, - const cudf::column_view& countCol, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - CUDF_EXPECTS( - countCol.type().id() == cudf::type_id::INT64, - "Decimal sum state requires INT64 count column"); - auto numRows = sumCol.size(); - CUDF_EXPECTS( - numRows == countCol.size(), - "Decimal sum state requires sum and count to be same size"); - CUDF_EXPECTS( - numRows <= std::numeric_limits::max(), - "Too many rows to serialize decimal sum state"); - - auto const rowCount = static_cast(numRows); - - auto const charsBytes = static_cast(numRows) * kStateSize; - auto const threshold = cudf::strings::get_offset64_threshold(); - auto const useLargeOffsets = charsBytes >= threshold; - CUDF_EXPECTS( - !useLargeOffsets || cudf::strings::is_large_strings_enabled(), - "Size of output exceeds the column size limit", - std::overflow_error); - - auto const offsetsType = - useLargeOffsets ? cudf::type_id::INT64 : cudf::type_id::INT32; - auto offsetsCol = cudf::make_fixed_width_column( - cudf::data_type{offsetsType}, - numRows + 1, - cudf::mask_state::UNALLOCATED, - stream); - auto offsetsView = offsetsCol->mutable_view(); - - rmm::device_buffer charsBuf( - static_cast(numRows) * kStateSize, stream); - - if (useLargeOffsets) { - launchFillOffsets(offsetsView.data(), rowCount, stream); - } else { - launchFillOffsets(offsetsView.data(), rowCount, stream); - } - - if (numRows > 0) { - auto charsPtr = reinterpret_cast(charsBuf.data()); - if (useLargeOffsets) { - auto offsetsPtr = offsetsView.data(); - if (sumCol.type().id() == cudf::type_id::DECIMAL64) { - launchPackState( - sumCol.data(), - countCol.data(), - offsetsPtr, - charsPtr, - rowCount, - stream); - } else { - CUDF_EXPECTS( - sumCol.type().id() == cudf::type_id::DECIMAL128, - "Unsupported decimal sum column type"); - launchPackState<__int128_t, int64_t>( - sumCol.data<__int128_t>(), - countCol.data(), - offsetsPtr, - charsPtr, - rowCount, - stream); - } - } else { - auto offsetsPtr = offsetsView.data(); - if (sumCol.type().id() == cudf::type_id::DECIMAL64) { - launchPackState( - sumCol.data(), - countCol.data(), - offsetsPtr, - charsPtr, - rowCount, - stream); - } else { - CUDF_EXPECTS( - sumCol.type().id() == cudf::type_id::DECIMAL128, - "Unsupported decimal sum column type"); - launchPackState<__int128_t, int32_t>( - sumCol.data<__int128_t>(), - countCol.data(), - offsetsPtr, - charsPtr, - rowCount, - stream); - } - } - } - - auto [nullMask, nullCount] = - buildStateValidityMask(sumCol, countCol, stream, mr); - return cudf::make_strings_column( - static_cast(numRows), - std::move(offsetsCol), - std::move(charsBuf), - nullCount, - std::move(nullMask)); -} - -std::unique_ptr computeDecimalAverage( - const cudf::column_view& sumCol, - const cudf::column_view& countCol, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - CUDF_EXPECTS( - countCol.type().id() == cudf::type_id::INT64, - "Decimal average requires INT64 count column"); - CUDF_EXPECTS( - sumCol.type().id() == cudf::type_id::DECIMAL64 || - sumCol.type().id() == cudf::type_id::DECIMAL128, - "Decimal average requires DECIMAL64 or DECIMAL128 sum column"); - CUDF_EXPECTS( - sumCol.size() == countCol.size(), - "Decimal average requires sum and count to be same size"); - - auto numRows = sumCol.size(); - auto out = cudf::make_fixed_width_column( - sumCol.type(), numRows, cudf::mask_state::UNALLOCATED, stream); - - if (numRows > 0) { - auto const rowCount = static_cast(numRows); - if (sumCol.type().id() == cudf::type_id::DECIMAL64) { - launchAvgRound( - sumCol.data(), - countCol.data(), - out->mutable_view().data(), - rowCount, - stream); - } else { - launchAvgRound<__int128_t>( - sumCol.data<__int128_t>(), - countCol.data(), - out->mutable_view().data<__int128_t>(), - rowCount, - stream); - } - } - - auto [nullMask, nullCount] = - buildStateValidityMask(sumCol, countCol, stream, mr); - if (nullCount > 0) { - out->set_null_mask(std::move(nullMask), nullCount); - } else if (nullMask.size() > 0) { - out->set_null_mask(std::move(nullMask), 0); - } - return out; -} - -} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu new file mode 100644 index 00000000000..e06f5422bf5 --- /dev/null +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu @@ -0,0 +1,328 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h" + +#include +#include +#include + +#include +#include +#include + +#include + +namespace facebook::velox::cudf_velox { +namespace { + +constexpr int32_t kStateSize = detail::kDecimalSumStateSize; + +struct DecimalSumStateDevice { + int64_t count; + int64_t overflow; + uint64_t lower; + int64_t upper; +}; + +static_assert(sizeof(DecimalSumStateDevice) == kStateSize); + +__device__ __forceinline__ void +splitToWords(int64_t value, int64_t& upper, uint64_t& lower) { + lower = static_cast(value); + upper = value < 0 ? -1 : 0; +} + +__device__ __forceinline__ void +splitToWords(__int128_t value, int64_t& upper, uint64_t& lower) { + lower = static_cast(value); + upper = static_cast(value >> 64); +} + +template +struct FillOffsetsFunctor { + OffsetT* offsets; + + __device__ void operator()(int32_t idx) const { + int64_t offset = static_cast(idx) * kStateSize; + offsets[idx] = static_cast(offset); + } +}; + +template +struct PackStateFunctor { + const SumT* sums; + const int64_t* counts; + const OffsetT* offsets; + uint8_t* chars; + + __device__ void operator()(int32_t idx) const { + int64_t offset = static_cast(offsets[idx]); + auto* state = reinterpret_cast(chars + offset); + int64_t upper; + uint64_t lower; + splitToWords(sums[idx], upper, lower); + state->count = counts[idx]; + state->overflow = 0; + state->lower = lower; + state->upper = upper; + } +}; + +template +struct UnpackStateFunctor { + const OffsetT* offsets; + const uint8_t* chars; + __int128_t* sums; + int64_t* counts; + + __device__ void operator()(int32_t idx) const { + int64_t offset = static_cast(offsets[idx]); + auto* state = + reinterpret_cast(chars + offset); + counts[idx] = state->count; + sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; + } +}; + +template +struct AvgRoundFunctor { + const SumT* sums; + const int64_t* counts; + SumT* out; + + __device__ void operator()(int32_t idx) const { + auto count = counts[idx]; + if (count == 0) { + out[idx] = SumT{0}; + return; + } + auto sum = sums[idx]; + SumT absSum = sum < 0 ? -sum : sum; + SumT half = static_cast(count / 2); + SumT rounded = (absSum + half) / static_cast(count); + out[idx] = sum < 0 ? -rounded : rounded; + } +}; + +template +void launchFillOffsets( + OffsetT* offsets, + int32_t numRows, + rmm::cuda_stream_view stream) { + FillOffsetsFunctor op{offsets}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), numRows + 1, op, stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); +} + +template +void launchPackState( + const SumT* sums, + const int64_t* counts, + const OffsetT* offsets, + uint8_t* chars, + int32_t numRows, + rmm::cuda_stream_view stream) { + PackStateFunctor op{sums, counts, offsets, chars}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), numRows, op, stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); +} + +template +void launchUnpackState( + const OffsetT* offsets, + const uint8_t* chars, + __int128_t* sums, + int64_t* counts, + int32_t numRows, + rmm::cuda_stream_view stream) { + UnpackStateFunctor op{offsets, chars, sums, counts}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), numRows, op, stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); +} + +template +void launchAvgRound( + const SumT* sums, + const int64_t* counts, + SumT* out, + int32_t numRows, + rmm::cuda_stream_view stream) { + AvgRoundFunctor op{sums, counts, out}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), numRows, op, stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); +} + +struct StateValidPredicate { + cudf::column_device_view sum; + cudf::column_device_view count; + + __device__ bool operator()(cudf::size_type idx) const { + if (sum.is_null(idx) || count.is_null(idx)) { + return false; + } + return count.element(idx) != 0; + } +}; + +std::pair buildStateValidityMaskImpl( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + auto numRows = sumCol.size(); + if (numRows == 0) { + return {rmm::device_buffer{}, 0}; + } + auto sumDeviceView = cudf::column_device_view::create(sumCol, stream); + auto countDeviceView = cudf::column_device_view::create(countCol, stream); + StateValidPredicate pred{*sumDeviceView, *countDeviceView}; + auto begin = thrust::make_counting_iterator(0); + auto end = begin + numRows; + return cudf::detail::valid_if(begin, end, pred, stream, mr); +} + +} // namespace + +namespace detail { + +void fillOffsetsForDecimalSumState( + bool use64BitOffsets, + void* offsetsMutable, + int32_t numRows, + rmm::cuda_stream_view stream) { + if (use64BitOffsets) { + launchFillOffsets(static_cast(offsetsMutable), numRows, stream); + } else { + launchFillOffsets(static_cast(offsetsMutable), numRows, stream); + } +} + +void packDecimalSumState( + cudf::type_id sumType, + bool use64BitOffsets, + const void* sumPtr, + const int64_t* countPtr, + const void* offsetsPtr, + uint8_t* chars, + int32_t numRows, + rmm::cuda_stream_view stream) { + if (use64BitOffsets) { + auto offsets = static_cast(offsetsPtr); + if (sumType == cudf::type_id::DECIMAL64) { + launchPackState( + static_cast(sumPtr), + countPtr, + offsets, + chars, + numRows, + stream); + } else { + launchPackState( + static_cast(sumPtr), + countPtr, + offsets, + chars, + numRows, + stream); + } + } else { + auto offsets = static_cast(offsetsPtr); + if (sumType == cudf::type_id::DECIMAL64) { + launchPackState( + static_cast(sumPtr), + countPtr, + offsets, + chars, + numRows, + stream); + } else { + launchPackState( + static_cast(sumPtr), + countPtr, + offsets, + chars, + numRows, + stream); + } + } +} + +void unpackDecimalSumState( + bool offsets64, + const void* offsetsPtr, + const uint8_t* chars, + __int128_t* sums, + int64_t* counts, + int32_t numRows, + rmm::cuda_stream_view stream) { + if (offsets64) { + launchUnpackState( + static_cast(offsetsPtr), + chars, + sums, + counts, + numRows, + stream); + } else { + launchUnpackState( + static_cast(offsetsPtr), + chars, + sums, + counts, + numRows, + stream); + } +} + +void averageRoundDecimalSum( + cudf::type_id sumType, + const void* sums, + const int64_t* counts, + void* out, + int32_t numRows, + rmm::cuda_stream_view stream) { + if (sumType == cudf::type_id::DECIMAL64) { + launchAvgRound( + static_cast(sums), + counts, + static_cast(out), + numRows, + stream); + } else { + launchAvgRound( + static_cast(sums), + counts, + static_cast<__int128_t*>(out), + numRows, + stream); + } +} + +std::pair buildStateValidityMask( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + return buildStateValidityMaskImpl(sumCol, countCol, stream, mr); +} + +} // namespace detail +} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h new file mode 100644 index 00000000000..5f2b0c1f8c7 --- /dev/null +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 +#include + +#include +#include +#include + +#include +#include + +namespace facebook::velox::cudf_velox::detail { + +/// Bytes per serialized row for decimal sum aggregation state. +constexpr int32_t kDecimalSumStateSize = 32; + +void fillOffsetsForDecimalSumState( + bool use64BitOffsets, + void* offsetsMutable, + int32_t numRows, + rmm::cuda_stream_view stream); + +void packDecimalSumState( + cudf::type_id sumType, + bool use64BitOffsets, + const void* sumPtr, + const int64_t* countPtr, + const void* offsetsPtr, + uint8_t* chars, + int32_t numRows, + rmm::cuda_stream_view stream); + +void unpackDecimalSumState( + bool offsets64, + const void* offsetsPtr, + const uint8_t* chars, + __int128_t* sums, + int64_t* counts, + int32_t numRows, + rmm::cuda_stream_view stream); + +void averageRoundDecimalSum( + cudf::type_id sumType, + const void* sums, + const int64_t* counts, + void* out, + int32_t numRows, + rmm::cuda_stream_view stream); + +std::pair buildStateValidityMask( + const cudf::column_view& sumCol, + const cudf::column_view& countCol, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); + +} // namespace facebook::velox::cudf_velox::detail From 1338cf8a5184863ac87041d1bc60c60d225a7428 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 12 May 2026 14:25:20 -0700 Subject: [PATCH 19/64] Refactor DecimalExpressionKernels in same manner as DecimalAggregationKernels Add function comments, per @devavret (cherry picked from commit 0dbc7ebb61d2290c75d52056f558c8c50d29a90c) --- .../cudf/expression/CMakeLists.txt | 2 +- .../expression/DecimalExpressionKernels.cpp | 172 +++++++++ .../expression/DecimalExpressionKernels.cu | 333 ------------------ .../expression/DecimalExpressionKernels.h | 19 +- .../expression/DecimalExpressionKernelsGpu.cu | 223 ++++++++++++ .../expression/DecimalExpressionKernelsGpu.h | 62 ++++ 6 files changed, 475 insertions(+), 336 deletions(-) delete mode 100644 velox/experimental/cudf/expression/DecimalExpressionKernels.cu create mode 100644 velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu create mode 100644 velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h diff --git a/velox/experimental/cudf/expression/CMakeLists.txt b/velox/experimental/cudf/expression/CMakeLists.txt index 1ea0fa9727b..c4c90233314 100644 --- a/velox/experimental/cudf/expression/CMakeLists.txt +++ b/velox/experimental/cudf/expression/CMakeLists.txt @@ -16,7 +16,7 @@ add_library( velox_cudf_expression AstExpression.cpp DecimalExpressionKernels.cpp - DecimalExpressionKernels.cu + DecimalExpressionKernelsGpu.cu ExpressionEvaluator.cpp JitExpression.cpp PrestoFunctions.cpp diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp b/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp index 84e4e72eb66..5a9e1a619ee 100644 --- a/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp +++ b/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp @@ -15,14 +15,49 @@ */ #include "velox/experimental/cudf/expression/AstPrinter.h" #include "velox/experimental/cudf/expression/DecimalExpressionKernels.h" +#include "velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h" #include "velox/common/base/Exceptions.h" #include +#include #include +#include +#include #include +#include namespace facebook::velox::cudf_velox { +namespace { + +__int128_t getDecimalScalarValue( + const cudf::scalar& s, + rmm::cuda_stream_view stream) { + if (s.type().id() == cudf::type_id::DECIMAL64) { + auto const& dec = + static_cast const&>(s); + return static_cast<__int128_t>(static_cast(dec.value(stream))); + } + auto const& dec = + static_cast const&>(s); + return static_cast<__int128_t>(dec.value(stream)); +} + +/// Column of \p outputType with \p size rows, all null (e.g. NULL scalar +/// operand). +std::unique_ptr makeAllNullDecimalColumn( + cudf::data_type outputType, + cudf::size_type size, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + if (size == 0) { + return cudf::make_empty_column(outputType); + } + return cudf::make_fixed_width_column( + outputType, size, cudf::mask_state::ALL_NULL, stream, mr); +} + +} // namespace // Scatters null values to positions where the divisor is zero. // Returns a new column with nulls at zero-divisor positions. @@ -68,4 +103,141 @@ std::unique_ptr scatterNullsAtZeroDivisor( *nullScalar, *result, divisorIsZero->view(), stream, mr); } +std::unique_ptr decimalDivide( + const cudf::column_view& lhs, + const cudf::column_view& rhs, + cudf::data_type outputType, + int32_t aRescale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + VELOX_CHECK(lhs.size() == rhs.size(), "Decimal divide requires equal sizes"); + // Use VELOX_CHECK (not _EQ) so failed checks do not pass cudf::type_id into + // fmt, which has no formatter for that enum. + VELOX_CHECK( + lhs.type().id() == rhs.type().id(), + "Decimal divide requires matching input types"); + VELOX_CHECK_GE( + aRescale, 0, "Decimal divide requires non-negative rescale factor"); + + const auto inType = lhs.type().id(); + const auto outType = outputType.id(); + VELOX_CHECK( + inType == cudf::type_id::DECIMAL64 || inType == cudf::type_id::DECIMAL128, + "Unsupported input type for decimal divide"); + if (inType == cudf::type_id::DECIMAL64) { + VELOX_CHECK( + outType == cudf::type_id::DECIMAL64 || + outType == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + } else { + VELOX_CHECK( + outType == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + } + + // Combine input null masks (lhs and rhs nulls). + auto [nullMask, nullCount] = + cudf::bitmask_and(cudf::table_view({lhs, rhs}), stream, mr); + + // Create output column with input null mask and perform division. + auto out = cudf::make_fixed_width_column( + outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); + + detail::launchDecimalDivideColumnColumn( + inType, outType, lhs, rhs, out->mutable_view(), aRescale, stream); + + // Scatter nulls where divisor is zero. + return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); +} + +std::unique_ptr decimalDivide( + const cudf::column_view& lhs, + const cudf::scalar& rhs, + cudf::data_type outputType, + int32_t aRescale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + VELOX_CHECK_GE( + aRescale, 0, "Decimal divide requires non-negative rescale factor"); + + if (!rhs.is_valid(stream)) { + return makeAllNullDecimalColumn(outputType, lhs.size(), stream, mr); + } + + auto nullMask = cudf::copy_bitmask(lhs, stream, mr); + auto nullCount = lhs.null_count(); + auto out = cudf::make_fixed_width_column( + outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); + + auto rhsValue = getDecimalScalarValue(rhs, stream); + + const auto inType = lhs.type().id(); + const auto outType = outputType.id(); + VELOX_CHECK( + inType == cudf::type_id::DECIMAL64 || inType == cudf::type_id::DECIMAL128, + "Unsupported input type for decimal divide"); + if (inType == cudf::type_id::DECIMAL64) { + VELOX_CHECK( + outType == cudf::type_id::DECIMAL64 || + outType == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + } else { + VELOX_CHECK( + outType == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + } + + detail::launchDecimalDivideColumnRhsScalar( + inType, outType, lhs, rhsValue, out->mutable_view(), aRescale, stream); + + return out; +} + +std::unique_ptr decimalDivide( + const cudf::scalar& lhs, + const cudf::column_view& rhs, + cudf::data_type outputType, + int32_t aRescale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + VELOX_CHECK_GE( + aRescale, 0, "Decimal divide requires non-negative rescale factor"); + + if (!lhs.is_valid(stream)) { + return makeAllNullDecimalColumn(outputType, rhs.size(), stream, mr); + } + + // Copy rhs null mask. + auto nullMask = cudf::copy_bitmask(rhs, stream, mr); + auto nullCount = rhs.null_count(); + + // Create output column and perform division. + auto out = cudf::make_fixed_width_column( + outputType, rhs.size(), std::move(nullMask), nullCount, stream, mr); + + auto lhsValue = getDecimalScalarValue(lhs, stream); + + const auto inType = rhs.type().id(); + const auto outType = outputType.id(); + VELOX_CHECK( + inType == cudf::type_id::DECIMAL64 || inType == cudf::type_id::DECIMAL128, + "Unsupported input type for decimal divide"); + if (inType == cudf::type_id::DECIMAL64) { + VELOX_CHECK( + outType == cudf::type_id::DECIMAL64 || + outType == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + } else { + VELOX_CHECK( + outType == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + } + + detail::launchDecimalDivideLhsScalarColumn( + inType, outType, lhsValue, rhs, out->mutable_view(), aRescale, stream); + + // Scatter nulls where divisor is zero. + return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); +} + } // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernels.cu b/velox/experimental/cudf/expression/DecimalExpressionKernels.cu deleted file mode 100644 index cd9291ff493..00000000000 --- a/velox/experimental/cudf/expression/DecimalExpressionKernels.cu +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * 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 "velox/experimental/cudf/expression/DecimalExpressionKernels.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -namespace facebook::velox::cudf_velox { -namespace { - -template -__device__ OutT -decimalDivideImpl(__int128_t numerator, __int128_t denom, __int128_t scale) { - if (denom == 0) { - return OutT{0}; - } - int sign = 1; - if (numerator < 0) { - numerator = -numerator; - sign = -sign; - } - if (denom < 0) { - denom = -denom; - sign = -sign; - } - __int128_t scaled = numerator * scale; - __int128_t quotient = scaled / denom; - __int128_t remainder = scaled % denom; - if (remainder * 2 >= denom) { - ++quotient; - } - if (sign < 0) { - quotient = -quotient; - } - return static_cast(quotient); -} - -inline __int128_t pow10Int128(int32_t exp) { - __int128_t value = 1; - for (int32_t i = 0; i < exp; ++i) { - value *= 10; - } - return value; -} - -template -struct DivideFunctor { - const InT* lhs; - const InT* rhs; - OutT* out; - __int128_t scale; - - __device__ void operator()(int32_t idx) const { - out[idx] = decimalDivideImpl(lhs[idx], rhs[idx], scale); - } -}; - -template -struct DivideLhsScalarFunctor { - __int128_t lhsValue; - const InColT* rhs; - OutT* out; - __int128_t scale; - - __device__ void operator()(int32_t idx) const { - out[idx] = decimalDivideImpl(lhsValue, rhs[idx], scale); - } -}; - -template -struct DivideRhsScalarFunctor { - const InColT* lhs; - __int128_t rhsValue; - OutT* out; - __int128_t scale; - - __device__ void operator()(int32_t idx) const { - out[idx] = decimalDivideImpl(lhs[idx], rhsValue, scale); - } -}; - -template -void launchDivideKernel( - const cudf::column_view& lhs, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (lhs.size() == 0) { - return; - } - DivideFunctor op{ - lhs.data(), - rhs.data(), - out.data(), - pow10Int128(aRescale)}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), lhs.size(), op, stream.value()); -} - -template -void launchDivideKernelLhsScalar( - __int128_t lhsValue, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (rhs.size() == 0) { - return; - } - DivideLhsScalarFunctor op{ - lhsValue, rhs.data(), out.data(), pow10Int128(aRescale)}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), rhs.size(), op, stream.value()); -} - -template -void launchDivideKernelRhsScalar( - const cudf::column_view& lhs, - __int128_t rhsValue, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (lhs.size() == 0) { - return; - } - DivideRhsScalarFunctor op{ - lhs.data(), rhsValue, out.data(), pow10Int128(aRescale)}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), lhs.size(), op, stream.value()); -} - -__int128_t getDecimalScalarValue( - const cudf::scalar& s, - rmm::cuda_stream_view stream) { - if (s.type().id() == cudf::type_id::DECIMAL64) { - auto const& dec = - static_cast const&>(s); - return static_cast<__int128_t>(static_cast(dec.value(stream))); - } - auto const& dec = - static_cast const&>(s); - return static_cast<__int128_t>(dec.value(stream)); -} - -/// Column of \p outputType with \p size rows, all null (e.g. NULL scalar -/// operand). -std::unique_ptr makeAllNullDecimalColumn( - cudf::data_type outputType, - cudf::size_type size, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - if (size == 0) { - return cudf::make_empty_column(outputType); - } - return cudf::make_fixed_width_column( - outputType, size, cudf::mask_state::ALL_NULL, stream, mr); -} - -} // namespace - -std::unique_ptr decimalDivide( - const cudf::column_view& lhs, - const cudf::column_view& rhs, - cudf::data_type outputType, - int32_t aRescale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - CUDF_EXPECTS(lhs.size() == rhs.size(), "Decimal divide requires equal sizes"); - CUDF_EXPECTS( - lhs.type().id() == rhs.type().id(), - "Decimal divide requires matching input types"); - CUDF_EXPECTS( - aRescale >= 0, "Decimal divide requires non-negative rescale factor"); - - // Combine input null masks (lhs and rhs nulls). - auto [nullMask, nullCount] = - cudf::bitmask_and(cudf::table_view({lhs, rhs}), stream, mr); - - // Create output column with input null mask and perform division. - auto out = cudf::make_fixed_width_column( - outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); - - if (lhs.type().id() == cudf::type_id::DECIMAL64) { - if (outputType.id() == cudf::type_id::DECIMAL64) { - launchDivideKernel( - lhs, rhs, out->mutable_view(), aRescale, stream); - } else { - CUDF_EXPECTS( - outputType.id() == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - launchDivideKernel( - lhs, rhs, out->mutable_view(), aRescale, stream); - } - } else { - CUDF_EXPECTS( - lhs.type().id() == cudf::type_id::DECIMAL128, - "Unsupported input type for decimal divide"); - CUDF_EXPECTS( - outputType.id() == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - launchDivideKernel<__int128_t, __int128_t>( - lhs, rhs, out->mutable_view(), aRescale, stream); - } - - // Scatter nulls where divisor is zero. - return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); -} - -std::unique_ptr decimalDivide( - const cudf::column_view& lhs, - const cudf::scalar& rhs, - cudf::data_type outputType, - int32_t aRescale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - CUDF_EXPECTS( - aRescale >= 0, "Decimal divide requires non-negative rescale factor"); - - if (!rhs.is_valid(stream)) { - return makeAllNullDecimalColumn(outputType, lhs.size(), stream, mr); - } - - auto nullMask = cudf::copy_bitmask(lhs, stream, mr); - auto nullCount = lhs.null_count(); - auto out = cudf::make_fixed_width_column( - outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); - - auto rhsValue = getDecimalScalarValue(rhs, stream); - - if (lhs.type().id() == cudf::type_id::DECIMAL64) { - if (outputType.id() == cudf::type_id::DECIMAL64) { - launchDivideKernelRhsScalar( - lhs, rhsValue, out->mutable_view(), aRescale, stream); - } else { - CUDF_EXPECTS( - outputType.id() == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - launchDivideKernelRhsScalar( - lhs, rhsValue, out->mutable_view(), aRescale, stream); - } - } else { - CUDF_EXPECTS( - lhs.type().id() == cudf::type_id::DECIMAL128, - "Unsupported input type for decimal divide"); - CUDF_EXPECTS( - outputType.id() == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - launchDivideKernelRhsScalar<__int128_t, __int128_t>( - lhs, rhsValue, out->mutable_view(), aRescale, stream); - } - - return out; -} - -std::unique_ptr decimalDivide( - const cudf::scalar& lhs, - const cudf::column_view& rhs, - cudf::data_type outputType, - int32_t aRescale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - CUDF_EXPECTS( - aRescale >= 0, "Decimal divide requires non-negative rescale factor"); - - if (!lhs.is_valid(stream)) { - return makeAllNullDecimalColumn(outputType, rhs.size(), stream, mr); - } - - // Copy rhs null mask. - auto nullMask = cudf::copy_bitmask(rhs, stream, mr); - auto nullCount = rhs.null_count(); - - // Create output column and perform division. - auto out = cudf::make_fixed_width_column( - outputType, rhs.size(), std::move(nullMask), nullCount, stream, mr); - - auto lhsValue = getDecimalScalarValue(lhs, stream); - - if (rhs.type().id() == cudf::type_id::DECIMAL64) { - if (outputType.id() == cudf::type_id::DECIMAL64) { - launchDivideKernelLhsScalar( - lhsValue, rhs, out->mutable_view(), aRescale, stream); - } else { - CUDF_EXPECTS( - outputType.id() == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - launchDivideKernelLhsScalar( - lhsValue, rhs, out->mutable_view(), aRescale, stream); - } - } else { - CUDF_EXPECTS( - rhs.type().id() == cudf::type_id::DECIMAL128, - "Unsupported input type for decimal divide"); - CUDF_EXPECTS( - outputType.id() == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - launchDivideKernelLhsScalar<__int128_t, __int128_t>( - lhsValue, rhs, out->mutable_view(), aRescale, stream); - } - - // Scatter nulls where divisor is zero. - return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); -} - -} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernels.h b/velox/experimental/cudf/expression/DecimalExpressionKernels.h index d7576ccdcf0..46b95d05cc5 100644 --- a/velox/experimental/cudf/expression/DecimalExpressionKernels.h +++ b/velox/experimental/cudf/expression/DecimalExpressionKernels.h @@ -26,6 +26,12 @@ namespace facebook::velox::cudf_velox { +// Element-wise decimal division of two columns (same DECIMAL64 or DECIMAL128 +// input type). Builds the output null mask as the bitwise AND of lhs and rhs +// validity, runs the GPU divide into outputType, and applies +// scatterNullsAtZeroDivisor so rows with a zero divisor are null. aRescale is +// the fixed-point scale adjustment (Velox passes outScale - lhsScale + +// rhsScale) used inside the kernel as a power-of-ten factor. std::unique_ptr decimalDivide( const cudf::column_view& lhs, const cudf::column_view& rhs, @@ -34,6 +40,9 @@ std::unique_ptr decimalDivide( rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); +// Like column/column decimalDivide, but rhs is a single decimal scalar. If the +// scalar is invalid, returns an all-null column of outputType; otherwise copies +// lhs nulls and divides without zero-divisor scattering (rhs is not per-row). std::unique_ptr decimalDivide( const cudf::column_view& lhs, const cudf::scalar& rhs, @@ -42,6 +51,10 @@ std::unique_ptr decimalDivide( rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); +// Like column/column decimalDivide, but lhs is a scalar and rhs is a column. +// Invalid lhs yields all-null output; otherwise rhs nulls are propagated, then +// divide and scatterNullsAtZeroDivisor on rhs so division-by-zero rows are +// null. std::unique_ptr decimalDivide( const cudf::scalar& lhs, const cudf::column_view& rhs, @@ -50,8 +63,10 @@ std::unique_ptr decimalDivide( rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); -// Helper function to scatter nulls at zero-divisor positions. -// Moved to .cpp file to allow use of VELOX_FAIL (incompatible with nvcc). +// After a decimal divide, forces output rows to null where the divisor column +// compares equal to zero (DECIMAL64 or DECIMAL128), using copy_if_else. Kept in +// the .cpp translation unit so it can use Velox checks alongside cuDF APIs +// without pulling those into CUDA compilation units. std::unique_ptr scatterNullsAtZeroDivisor( std::unique_ptr result, const cudf::column_view& divisor, diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu new file mode 100644 index 00000000000..d6a6003d0d3 --- /dev/null +++ b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu @@ -0,0 +1,223 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h" + +#include + +#include +#include +#include + +#include + +namespace facebook::velox::cudf_velox { +namespace { + +template +__device__ OutT +decimalDivideImpl(__int128_t numerator, __int128_t denom, __int128_t scale) { + if (denom == 0) { + return OutT{0}; + } + int sign = 1; + if (numerator < 0) { + numerator = -numerator; + sign = -sign; + } + if (denom < 0) { + denom = -denom; + sign = -sign; + } + __int128_t scaled = numerator * scale; + __int128_t quotient = scaled / denom; + __int128_t remainder = scaled % denom; + if (remainder * 2 >= denom) { + ++quotient; + } + if (sign < 0) { + quotient = -quotient; + } + return static_cast(quotient); +} + +inline __int128_t pow10Int128(int32_t exp) { + __int128_t value = 1; + for (int32_t i = 0; i < exp; ++i) { + value *= 10; + } + return value; +} + +template +struct DivideFunctor { + const InT* lhs; + const InT* rhs; + OutT* out; + __int128_t scale; + + __device__ void operator()(int32_t idx) const { + out[idx] = decimalDivideImpl(lhs[idx], rhs[idx], scale); + } +}; + +template +struct DivideLhsScalarFunctor { + __int128_t lhsValue; + const InColT* rhs; + OutT* out; + __int128_t scale; + + __device__ void operator()(int32_t idx) const { + out[idx] = decimalDivideImpl(lhsValue, rhs[idx], scale); + } +}; + +template +struct DivideRhsScalarFunctor { + const InColT* lhs; + __int128_t rhsValue; + OutT* out; + __int128_t scale; + + __device__ void operator()(int32_t idx) const { + out[idx] = decimalDivideImpl(lhs[idx], rhsValue, scale); + } +}; + +template +void launchDivideKernel( + const cudf::column_view& lhs, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (lhs.size() == 0) { + return; + } + DivideFunctor op{ + lhs.data(), + rhs.data(), + out.data(), + pow10Int128(aRescale)}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), lhs.size(), op, stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); +} + +template +void launchDivideKernelLhsScalar( + __int128_t lhsValue, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (rhs.size() == 0) { + return; + } + DivideLhsScalarFunctor op{ + lhsValue, rhs.data(), out.data(), pow10Int128(aRescale)}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), rhs.size(), op, stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); +} + +template +void launchDivideKernelRhsScalar( + const cudf::column_view& lhs, + __int128_t rhsValue, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (lhs.size() == 0) { + return; + } + DivideRhsScalarFunctor op{ + lhs.data(), rhsValue, out.data(), pow10Int128(aRescale)}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), lhs.size(), op, stream.value()); + CUDF_CUDA_TRY(cudaGetLastError()); +} + +} // namespace + +namespace detail { + +void launchDecimalDivideColumnColumn( + cudf::type_id inType, + cudf::type_id outType, + const cudf::column_view& lhs, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (inType == cudf::type_id::DECIMAL64) { + if (outType == cudf::type_id::DECIMAL64) { + launchDivideKernel(lhs, rhs, out, aRescale, stream); + } else { + launchDivideKernel(lhs, rhs, out, aRescale, stream); + } + } else { + launchDivideKernel<__int128_t, __int128_t>(lhs, rhs, out, aRescale, stream); + } +} + +void launchDecimalDivideColumnRhsScalar( + cudf::type_id inType, + cudf::type_id outType, + const cudf::column_view& lhs, + __int128_t rhsValue, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (inType == cudf::type_id::DECIMAL64) { + if (outType == cudf::type_id::DECIMAL64) { + launchDivideKernelRhsScalar( + lhs, rhsValue, out, aRescale, stream); + } else { + launchDivideKernelRhsScalar( + lhs, rhsValue, out, aRescale, stream); + } + } else { + launchDivideKernelRhsScalar<__int128_t, __int128_t>( + lhs, rhsValue, out, aRescale, stream); + } +} + +void launchDecimalDivideLhsScalarColumn( + cudf::type_id inType, + cudf::type_id outType, + __int128_t lhsValue, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (inType == cudf::type_id::DECIMAL64) { + if (outType == cudf::type_id::DECIMAL64) { + launchDivideKernelLhsScalar( + lhsValue, rhs, out, aRescale, stream); + } else { + launchDivideKernelLhsScalar( + lhsValue, rhs, out, aRescale, stream); + } + } else { + launchDivideKernelLhsScalar<__int128_t, __int128_t>( + lhsValue, rhs, out, aRescale, stream); + } +} + +} // namespace detail +} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h new file mode 100644 index 00000000000..2d20360c41b --- /dev/null +++ b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 +#include + +#include + +#include + +namespace facebook::velox::cudf_velox::detail { + +// Dispatches a per-row device loop: fixed-point divide (lhs * 10^aRescale) / +// rhs with half-away-from-zero rounding on the remainder, writing into out. +// Zero divisors produce a numeric zero in out (callers patch nulls). inType / +// outType select DECIMAL64 vs DECIMAL128 storage widths for inputs and result. +void launchDecimalDivideColumnColumn( + cudf::type_id inType, + cudf::type_id outType, + const cudf::column_view& lhs, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream); + +// Same kernel math as launchDecimalDivideColumnColumn, but rhs is a single +// __int128_t decimal payload (already decoded from a cuDF scalar). +void launchDecimalDivideColumnRhsScalar( + cudf::type_id inType, + cudf::type_id outType, + const cudf::column_view& lhs, + __int128_t rhsValue, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream); + +// Same kernel math as launchDecimalDivideColumnColumn, but lhs is a single +// __int128_t decimal payload and rhs is per-row. +void launchDecimalDivideLhsScalarColumn( + cudf::type_id inType, + cudf::type_id outType, + __int128_t lhsValue, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream); + +} // namespace facebook::velox::cudf_velox::detail From b21a93e154fde358a0b41b0a5836d5a3e24de233 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 12 May 2026 16:50:52 -0700 Subject: [PATCH 20/64] Simplify some overcomplex and inconsistent generated code Add function comments, per @devavret (cherry picked from commit dc2a3a9ecbf06906c8640b37619e2eee195bd63f) --- velox/experimental/cudf/exec/CudfGroupby.cpp | 18 +++++--- velox/experimental/cudf/exec/CudfReduce.cpp | 24 ++++------ .../cudf/exec/DecimalAggregationCommon.h | 15 +++++- .../cudf/exec/DecimalAggregationKernels.cpp | 12 +---- .../cudf/exec/DecimalAggregationKernels.h | 25 +++++++--- .../cudf/exec/DecimalAggregationKernelsGpu.h | 18 +++++++- .../cudf/tests/DecimalAggregationTest.cpp | 46 ++++++++++--------- 7 files changed, 93 insertions(+), 65 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 6464a858d7a..af120734142 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -94,9 +94,12 @@ DEFINE_SIMPLE_GROUPBY_AGGREGATOR(Max, max, MAX) // Decimal SUM and AVG aggregators are separate implementations, as they need to // handle the VARBINARY encoded intermediate state for streaming aggregation. -// Unlike other aggregators, these classes hold state (the decoded intermediate -// sum and count columns and associated indices) in order to guarantee a -// lifetime constraint between aggregation steps. +// Due to the packing and unpacking of that intermediate state, and the special +// handling required for the decimal divide, we cannot just use the existing +// cudf::make_mean_aggregation class. Also, unlike other aggregators, these +// classes hold state (the decoded intermediate sum and count columns and +// associated indices) in order to guarantee a lifetime constraint between +// aggregation steps. void addDecimalSumCountRequestsAfterDecode( cudf::column_view encodedColumn, @@ -107,10 +110,10 @@ void addDecimalSumCountRequestsAfterDecode( uint32_t& countIdx, std::unique_ptr& decodedSum, std::unique_ptr& decodedCount) { - auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + auto sumAndCount = cudf_velox::deserializeDecimalSumState( encodedColumn, scale, stream, cudf_velox::get_output_mr()); - decodedSum = std::move(decoded.sum); - decodedCount = std::move(decoded.count); + decodedSum.swap(sumAndCount.sum); + decodedCount.swap(sumAndCount.count); sumIdx = requests.size(); auto& sumRequest = requests.emplace_back(); @@ -185,8 +188,9 @@ void addDecimalFinalSumOnlyRequest( auto scale = getDecimalPrecisionScale(*resultType).second; auto& request = requests.emplace_back(); sumIdx = requests.size() - 1; - decodedSum = cudf_velox::deserializeDecimalSumState( + auto sumAndCount = cudf_velox::deserializeDecimalSumState( tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); + decodedSum.swap(sumAndCount.sum); request.values = decodedSum->view(); request.aggregations.push_back( cudf::make_sum_aggregation()); diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 3482bdfbe8b..a3aed83b59f 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -279,16 +279,16 @@ std::unique_ptr intermediateDecimalMergeSerializedString( int32_t scale, rmm::cuda_stream_view stream) { auto const sumAgg = cudf::make_sum_aggregation(); - auto decoded = cudf_velox::deserializeDecimalSumStateWithCount( + auto sumAndCount = cudf_velox::deserializeDecimalSumState( inputCol, scale, stream, get_output_mr()); auto sumScalar = cudf::reduce( - decoded.sum->view(), + sumAndCount.sum->view(), *sumAgg, - decoded.sum->view().type(), + sumAndCount.sum->view().type(), stream, get_temp_mr()); auto countScalar = cudf::reduce( - decoded.count->view(), + sumAndCount.count->view(), *sumAgg, cudf::data_type{cudf::type_id::INT64}, stream, @@ -307,7 +307,7 @@ std::unique_ptr finalDecimalAvgFromSerializedString( TypePtr const& resultType, rmm::cuda_stream_view stream) { auto const sumAgg = cudf::make_sum_aggregation(); - auto sumAndCount = cudf_velox::deserializeDecimalSumStateWithCount( + auto sumAndCount = cudf_velox::deserializeDecimalSumState( inputCol, scale, stream, get_output_mr()); auto sumScalar = cudf::reduce( sumAndCount.sum->view(), @@ -329,14 +329,6 @@ std::unique_ptr finalDecimalAvgFromSerializedString( std::move(sumCol), std::move(countCol), resultType, stream); } -std::unique_ptr finalDecimalSumDecodeColumn( - cudf::column_view inputCol, - int32_t scale, - rmm::cuda_stream_view stream) { - return cudf_velox::deserializeDecimalSumState( - inputCol, scale, stream, get_output_mr()); -} - std::unique_ptr singleDecimalAvgFromRawColumn( cudf::column_view inputCol, TypePtr const& resultType, @@ -394,8 +386,10 @@ std::unique_ptr reduceFinalDecimalSumFromSerializedColumn( rmm::cuda_stream_view stream) { validateIntermediateColumnType(inputCol); auto scale = getDecimalPrecisionScale(*outputType).second; - auto decodedSum = finalDecimalSumDecodeColumn(inputCol, scale, stream); - return singleOrRawDecimalSumWithCast(decodedSum->view(), outputType, stream); + auto sumAndCount = cudf_velox::deserializeDecimalSumState( + inputCol, scale, stream, get_output_mr()); + return singleOrRawDecimalSumWithCast( + sumAndCount.sum->view(), outputType, stream); } std::unique_ptr reduceFinalDecimalAvgFromSerializedColumn( diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.h b/velox/experimental/cudf/exec/DecimalAggregationCommon.h index 35e8b9e4b12..20dec2e6449 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.h +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.h @@ -26,19 +26,30 @@ namespace facebook::velox::cudf_velox { -/// Checks that the column is STRING-encoded serialized decimal aggregation -/// state. +// Asserts that a column holds serialized decimal aggregate state in the form +// Velox uses for VARBINARY: a cuDF STRING column whose bytes are the packed +// sum/count payloads (see serializeDecimalSumState). void validateIntermediateColumnType(cudf::column_view const& column); +// Ensures the partial-row count column is INT64, casting with the default GPU +// output memory resource when the incoming type differs. std::unique_ptr castCountColumnToInt64( std::unique_ptr count, rmm::cuda_stream_view stream); +// Normalizes the count column to INT64, then encodes sum and count into a +// single STRING column of fixed-width per-row payloads (delegates to +// serializeDecimalSumState). Used when emitting or persisting partial / +// intermediate decimal SUM state for the cuDF path. std::unique_ptr serializeDecimalPartialOrIntermediateState( std::unique_ptr sum, std::unique_ptr count, rmm::cuda_stream_view stream); +// Normalizes the count column to INT64, computes a per-row decimal average +// from intermediate sum/count (delegates to computeDecimalAverage), then casts +// the result to the Velox result type when its cuDF decimal encoding differs +// from the average column's type. std::unique_ptr finalizeDecimalAverage( std::unique_ptr sum, std::unique_ptr count, diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index 724903f3962..99b20ff573f 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -28,7 +28,7 @@ namespace facebook::velox::cudf_velox { -DecimalSumStateColumns deserializeDecimalSumStateWithCount( +DecimalSumStateColumns deserializeDecimalSumState( const cudf::column_view& stateCol, int32_t scale, rmm::cuda_stream_view stream, @@ -121,16 +121,6 @@ DecimalSumStateColumns deserializeDecimalSumStateWithCount( return result; } -std::unique_ptr deserializeDecimalSumState( - const cudf::column_view& stateCol, - int32_t scale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - auto decoded = - deserializeDecimalSumStateWithCount(stateCol, scale, stream, mr); - return std::move(decoded.sum); -} - std::unique_ptr serializeDecimalSumState( const cudf::column_view& sumCol, const cudf::column_view& countCol, diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.h b/velox/experimental/cudf/exec/DecimalAggregationKernels.h index 620194249f2..3a0c6eeddba 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.h +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.h @@ -29,24 +29,35 @@ struct DecimalSumStateColumns { std::unique_ptr count; }; -DecimalSumStateColumns deserializeDecimalSumStateWithCount( - const cudf::column_view& stateCol, - int32_t scale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr); - -std::unique_ptr deserializeDecimalSumState( +// Decodes intermediate decimal SUM aggregate state stored as a cuDF STRING +// column (fixed-size packed bytes per row, converted from Velox VARBINARY) into +// two device columns: a DECIMAL128 sum with scale -scale (matching Velox +// intermediate state) and an INT64 partial row count. Handles empty input, +// all-null state without touching payload buffers, and propagates the source +// null mask to both outputs when present. +DecimalSumStateColumns deserializeDecimalSumState( const cudf::column_view& stateCol, int32_t scale, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); +// Encodes partial decimal SUM state (DECIMAL64 or DECIMAL128 sums plus +// INT64 counts) into a single STRING column (later converted to Velox +// VARBINARY): per-row fixed-width payloads and string offsets (INT32 or INT64 +// depending on total char size and cuDF large-strings settings). The output +// null mask matches buildStateValidityMask: a row is invalid if the sum or +// count is null, or the count is zero. std::unique_ptr serializeDecimalSumState( const cudf::column_view& sumCol, const cudf::column_view& countCol, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); +// Finalizes AVG from intermediate SUM state: divides each sum by its count +// on device with decimal-specific rounding (see averageRoundDecimalSum), +// producing a column of the same decimal type as the sum. Rows are null +// where buildStateValidityMask marks them invalid (null sum/count or zero +// count), matching serializeDecimalSumState. std::unique_ptr computeDecimalAverage( const cudf::column_view& sumCol, const cudf::column_view& countCol, diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h index 5f2b0c1f8c7..0c44c11010e 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h @@ -27,15 +27,22 @@ namespace facebook::velox::cudf_velox::detail { -/// Bytes per serialized row for decimal sum aggregation state. +// Size in bytes of each row's packed decimal SUM intermediate state in the +// strings payload (count, overflow placeholder, and 128-bit sum split into +// words). constexpr int32_t kDecimalSumStateSize = 32; +// Writes strings-style prefix offsets into offsetsMutable for numRows + 1 +// entries: offset[i] == i * kDecimalSumStateSize (INT32 or INT64 elements). void fillOffsetsForDecimalSumState( bool use64BitOffsets, void* offsetsMutable, int32_t numRows, rmm::cuda_stream_view stream); +// For each row, writes kDecimalSumStateSize bytes into chars at the byte offset +// given by offsetsPtr, encoding the partial sum (DECIMAL64 or DECIMAL128) and +// int64 count into the device struct layout used for VARBINARY interchange. void packDecimalSumState( cudf::type_id sumType, bool use64BitOffsets, @@ -46,6 +53,8 @@ void packDecimalSumState( int32_t numRows, rmm::cuda_stream_view stream); +// Inverse of packDecimalSumState: reads fixed-width payloads via offsetsPtr +// (INT32 or INT64 string offsets) and fills per-row DECIMAL128 sums and counts. void unpackDecimalSumState( bool offsets64, const void* offsetsPtr, @@ -55,6 +64,10 @@ void unpackDecimalSumState( int32_t numRows, rmm::cuda_stream_view stream); +// Per-row average from intermediate sum/count: integer divide of abs(sum) by +// count with half-up bias (add count/2 before dividing), then restore sign; +// count == 0 writes a numeric zero (validity is applied separately). Output +// element type matches sumType (DECIMAL64 or DECIMAL128). void averageRoundDecimalSum( cudf::type_id sumType, const void* sums, @@ -63,6 +76,9 @@ void averageRoundDecimalSum( int32_t numRows, rmm::cuda_stream_view stream); +// Builds a bitmask for rows where both sum and count are valid and count is +// non-zero (via cudf::detail::valid_if), for use when serializing state or +// finalizing averages. std::pair buildStateValidityMask( const cudf::column_view& sumCol, const cudf::column_view& countCol, diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index 6734025e752..a6bd2166b29 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -997,12 +997,13 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); - auto sumOnly = deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto sumAndCount = + deserializeDecimalSumState(stateCol->view(), 2, stream, mr); auto stateMask = copyNullMask(stateCol->view(), stream); - auto sumMask = copyNullMask(sumOnly->view(), stream); + auto sumMask = copyNullMask(sumAndCount.sum->view(), stream); EXPECT_EQ(stateMask, sumMask); - auto outSum = copyColumnData<__int128_t>(sumOnly->view(), stream); + auto outSum = copyColumnData<__int128_t>(sumAndCount.sum->view(), stream); for (size_t i = 0; i < sums.size(); ++i) { bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; EXPECT_EQ(isValidAt(sumMask, i), expectedValid); @@ -1028,12 +1029,13 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal128) { auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); - auto sumOnly = deserializeDecimalSumState(stateCol->view(), 3, stream, mr); + auto sumAndCount = + deserializeDecimalSumState(stateCol->view(), 3, stream, mr); auto stateMask = copyNullMask(stateCol->view(), stream); - auto sumMask = copyNullMask(sumOnly->view(), stream); + auto sumMask = copyNullMask(sumAndCount.sum->view(), stream); EXPECT_EQ(stateMask, sumMask); - auto outSum = copyColumnData<__int128_t>(sumOnly->view(), stream); + auto outSum = copyColumnData<__int128_t>(sumAndCount.sum->view(), stream); for (size_t i = 0; i < sums.size(); ++i) { bool expectedValid = sumValid[i] && countValid[i] && counts[i] != 0; EXPECT_EQ(isValidAt(sumMask, i), expectedValid); @@ -1072,10 +1074,10 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateAllNull) { nullCount, std::move(nullMask)); - auto decoded = - deserializeDecimalSumStateWithCount(stateCol->view(), 2, stream, mr); - auto outSumView = decoded.sum->view(); - auto outCountView = decoded.count->view(); + auto sumAndCount = + deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto outSumView = sumAndCount.sum->view(); + auto outCountView = sumAndCount.count->view(); EXPECT_EQ(outSumView.size(), numRows); EXPECT_EQ(outCountView.size(), numRows); @@ -1127,10 +1129,10 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { cudf::strings_column_view strings(stateCol->view()); EXPECT_EQ(strings.offsets().type().id(), cudf::type_id::INT64); - auto decoded = - deserializeDecimalSumStateWithCount(stateCol->view(), 2, stream, mr); - auto outSumView = decoded.sum->view(); - auto outCountView = decoded.count->view(); + auto sumAndCount = + deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto outSumView = sumAndCount.sum->view(); + auto outCountView = sumAndCount.count->view(); auto outSum = copyColumnData<__int128_t>(outSumView, stream); auto outCount = copyColumnData(outCountView, stream); auto outSumMask = copyNullMask(outSumView, stream); @@ -1235,10 +1237,10 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); auto stateMask = copyNullMask(stateCol->view(), stream); - auto decoded = - deserializeDecimalSumStateWithCount(stateCol->view(), 2, stream, mr); - auto outSumView = decoded.sum->view(); - auto outCountView = decoded.count->view(); + auto sumAndCount = + deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto outSumView = sumAndCount.sum->view(); + auto outCountView = sumAndCount.count->view(); auto outSum = copyColumnData<__int128_t>(outSumView, stream); auto outCount = copyColumnData(outCountView, stream); auto outSumMask = copyNullMask(outSumView, stream); @@ -1275,10 +1277,10 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal128) { serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); auto stateMask = copyNullMask(stateCol->view(), stream); - auto decoded = - deserializeDecimalSumStateWithCount(stateCol->view(), 3, stream, mr); - auto outSumView = decoded.sum->view(); - auto outCountView = decoded.count->view(); + auto sumAndCount = + deserializeDecimalSumState(stateCol->view(), 3, stream, mr); + auto outSumView = sumAndCount.sum->view(); + auto outCountView = sumAndCount.count->view(); auto outSum = copyColumnData<__int128_t>(outSumView, stream); auto outCount = copyColumnData(outCountView, stream); auto outSumMask = copyNullMask(outSumView, stream); From b1975cb4dfc532d7cd7c7a730ca3af03078f9895 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Wed, 13 May 2026 12:33:48 -0700 Subject: [PATCH 21/64] Fix after merge update --- velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp b/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp index fc19ec83c81..2d4a3687239 100644 --- a/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp +++ b/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp @@ -727,9 +727,9 @@ std::unique_ptr CudfNestedLoopJoinProbe::emitProbeMismatchRows( for (size_t i = 0; i < buildColumnOutputIndices_.size(); ++i) { auto outIdx = buildColumnOutputIndices_[i]; auto buildChannel = buildColumnIndicesToGather_[i]; - auto buildCudfType = veloxToCudfTypeId(buildType_->childAt(buildChannel)); + auto buildCudfDataType = veloxToCudfDataType(buildType_->childAt(buildChannel)); auto nullScalar = cudf::make_default_constructed_scalar( - cudf::data_type{buildCudfType}, stream, get_temp_mr()); + buildCudfDataType, stream, get_temp_mr()); outCols[outIdx] = cudf::make_column_from_scalar( *nullScalar, numUnmatched, stream, get_output_mr()); } @@ -772,9 +772,9 @@ RowVectorPtr CudfNestedLoopJoinProbe::emitBuildMismatchRows( for (size_t li = 0; li < probeColumnOutputIndices_.size(); ++li) { auto outIdx = probeColumnOutputIndices_[li]; auto probeChannel = probeColumnIndicesToGather_[li]; - auto probeCudfType = veloxToCudfTypeId(probeType_->childAt(probeChannel)); + auto probeCudfDataType = veloxToCudfDataType(probeType_->childAt(probeChannel)); auto nullScalar = cudf::make_default_constructed_scalar( - cudf::data_type{probeCudfType}, stream, get_temp_mr()); + probeCudfDataType, stream, get_temp_mr()); outCols[outIdx] = cudf::make_column_from_scalar( *nullScalar, numUnmatched, stream, get_output_mr()); } From e49f979b1afddf8e0de8c95d770bf6c318eb69e8 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Wed, 13 May 2026 13:49:07 -0700 Subject: [PATCH 22/64] Format. One day I'll remember do this before committing... --- velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp b/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp index 2d4a3687239..da25e039c67 100644 --- a/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp +++ b/velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp @@ -727,7 +727,8 @@ std::unique_ptr CudfNestedLoopJoinProbe::emitProbeMismatchRows( for (size_t i = 0; i < buildColumnOutputIndices_.size(); ++i) { auto outIdx = buildColumnOutputIndices_[i]; auto buildChannel = buildColumnIndicesToGather_[i]; - auto buildCudfDataType = veloxToCudfDataType(buildType_->childAt(buildChannel)); + auto buildCudfDataType = + veloxToCudfDataType(buildType_->childAt(buildChannel)); auto nullScalar = cudf::make_default_constructed_scalar( buildCudfDataType, stream, get_temp_mr()); outCols[outIdx] = cudf::make_column_from_scalar( @@ -772,7 +773,8 @@ RowVectorPtr CudfNestedLoopJoinProbe::emitBuildMismatchRows( for (size_t li = 0; li < probeColumnOutputIndices_.size(); ++li) { auto outIdx = probeColumnOutputIndices_[li]; auto probeChannel = probeColumnIndicesToGather_[li]; - auto probeCudfDataType = veloxToCudfDataType(probeType_->childAt(probeChannel)); + auto probeCudfDataType = + veloxToCudfDataType(probeType_->childAt(probeChannel)); auto nullScalar = cudf::make_default_constructed_scalar( probeCudfDataType, stream, get_temp_mr()); outCols[outIdx] = cudf::make_column_from_scalar( From c29f7d070f8bba9f5ab90919c7889eb4a11cae15 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 10:10:57 -0700 Subject: [PATCH 23/64] Comment DecimalSumState fields, per @shrshi, and simplify --- .../cudf/exec/DecimalAggregationKernelsGpu.cu | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu index e06f5422bf5..07bd2b39df1 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu @@ -29,16 +29,15 @@ namespace facebook::velox::cudf_velox { namespace { -constexpr int32_t kStateSize = detail::kDecimalSumStateSize; - -struct DecimalSumStateDevice { - int64_t count; - int64_t overflow; - uint64_t lower; - int64_t upper; +// TODO: Handle overflow as in CPU. +struct DecimalSumState { + int64_t count; // count of non-null input rows aggregated + int64_t overflow; // overflow/extension field, not used by GPU + uint64_t lower; // lower 64 bits of the decimal sum + int64_t upper; // upper 64 bits of the decimal sum (signed) }; -static_assert(sizeof(DecimalSumStateDevice) == kStateSize); +static_assert(sizeof(DecimalSumState) == detail::kDecimalSumStateSize); __device__ __forceinline__ void splitToWords(int64_t value, int64_t& upper, uint64_t& lower) { @@ -57,7 +56,7 @@ struct FillOffsetsFunctor { OffsetT* offsets; __device__ void operator()(int32_t idx) const { - int64_t offset = static_cast(idx) * kStateSize; + int64_t offset = static_cast(idx) * detail::kDecimalSumStateSize; offsets[idx] = static_cast(offset); } }; @@ -71,7 +70,7 @@ struct PackStateFunctor { __device__ void operator()(int32_t idx) const { int64_t offset = static_cast(offsets[idx]); - auto* state = reinterpret_cast(chars + offset); + auto* state = reinterpret_cast(chars + offset); int64_t upper; uint64_t lower; splitToWords(sums[idx], upper, lower); @@ -92,7 +91,7 @@ struct UnpackStateFunctor { __device__ void operator()(int32_t idx) const { int64_t offset = static_cast(offsets[idx]); auto* state = - reinterpret_cast(chars + offset); + reinterpret_cast(chars + offset); counts[idx] = state->count; sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; } From c2e1f906aaf005d02c4855405846f8f053f493c5 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 10:34:16 -0700 Subject: [PATCH 24/64] Use VELOX_CHECK_EQ, per @karthikeyann --- .../experimental/cudf/exec/DecimalAggregationCommon.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp index 95d5c7a88fc..72e96e34a96 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -26,9 +26,12 @@ namespace facebook::velox::cudf_velox { void validateIntermediateColumnType(cudf::column_view const& column) { - VELOX_CHECK( - column.type().id() == cudf::type_id::STRING, - "Expected serialized decimal aggregation state: Velox VARBINARY represented as cuDF STRING"); + // fmt does not understand cudf::type_id enum class + auto const colType = static_cast(column.type().id()); + VELOX_CHECK_EQ( + colType, + static_cast(cudf::type_id::STRING), + "Expected serialized decimal aggregation state: Velox VARBINARY represented as cuDF STRING (got type {})", colType); } std::unique_ptr castCountColumnToInt64( From 0a84963fcb83a7ace1e142e8949851b41f60f4e4 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 10:34:52 -0700 Subject: [PATCH 25/64] outputType/resultType resolution, per @karthikeyann --- velox/experimental/cudf/exec/CudfReduce.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index a3aed83b59f..0f0a55b4c37 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -395,12 +395,11 @@ std::unique_ptr reduceFinalDecimalSumFromSerializedColumn( std::unique_ptr reduceFinalDecimalAvgFromSerializedColumn( cudf::column_view inputCol, TypePtr const& outputType, - TypePtr const& resultType, rmm::cuda_stream_view stream) { validateIntermediateColumnType(inputCol); auto scale = getDecimalPrecisionScale(*outputType).second; return finalDecimalAvgFromSerializedString( - inputCol, scale, resultType, stream); + inputCol, scale, outputType, stream); } struct ReduceDecimalSumAggregator : ReduceAggregator { @@ -457,8 +456,9 @@ struct ReduceDecimalAvgAggregator : ReduceAggregator { return reduceIntermediateDecimalFromSerializedColumn( inputCol, outputType, stream); case core::AggregationNode::Step::kFinal: + VELOX_CHECK(outputType == resultType, "outputType/resultType mismatch"); return reduceFinalDecimalAvgFromSerializedColumn( - inputCol, outputType, resultType, stream); + inputCol, outputType, stream); default: VELOX_NYI("Unsupported aggregation step for decimal avg reduce"); } From 486a0a60f695c642129a0a5f87906cc589501c38 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 11:10:47 -0700 Subject: [PATCH 26/64] Skip adding null mask if no nulls, add tests to validate, per @karthikeyann --- .../cudf/exec/DecimalAggregationKernels.cpp | 2 - .../cudf/tests/DecimalAggregationTest.cpp | 141 ++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index 99b20ff573f..e881ebf1d15 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -242,8 +242,6 @@ std::unique_ptr computeDecimalAverage( detail::buildStateValidityMask(sumCol, countCol, stream, mr); if (nullCount > 0) { out->set_null_mask(std::move(nullMask), nullCount); - } else if (nullMask.size() > 0) { - out->set_null_mask(std::move(nullMask), 0); } return out; } diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index a6bd2166b29..644e89766ab 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -1223,6 +1223,147 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { } } +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector sums = {100, 200, -150}; + std::vector counts = {4, 5, 3}; + std::vector sumValid = {true, true, true}; + std::vector countValid = {true, true, true}; + + auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + + EXPECT_EQ(avgCol->view().null_count(), 0); + auto avgMask = copyNullMask(avgCol->view(), stream); + auto outAvg = copyColumnData(avgCol->view(), stream); + + auto avgUnscaled = [](int128_t sum, int64_t count) { + __int128_t out = 0; + facebook::velox::DecimalUtil:: + divideWithRoundUp<__int128_t, __int128_t, int64_t>( + out, sum, count, false, 0, 0); + return static_cast(out); + }; + + for (size_t i = 0; i < sums.size(); ++i) { + if (avgCol->view().nullable()) { + EXPECT_TRUE(isValidAt(avgMask, i)) << "row " << i; + } + EXPECT_EQ(outAvg[i], avgUnscaled(sums[i], counts[i])) << "row " << i; + } +} + +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector<__int128_t> sums = { + static_cast<__int128_t>(90000), + static_cast<__int128_t>(-5000), + static_cast<__int128_t>(1), + }; + std::vector counts = {3, 5, 1}; + std::vector sumValid = {true, true, true}; + std::vector countValid = {true, true, true}; + + auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); + auto countCol = makeInt64Column(counts, &countValid, stream); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + + EXPECT_EQ(avgCol->view().null_count(), 0); + auto avgMask = copyNullMask(avgCol->view(), stream); + auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); + + auto avgUnscaled = [](int128_t sum, int64_t count) { + __int128_t out = 0; + facebook::velox::DecimalUtil:: + divideWithRoundUp<__int128_t, __int128_t, int64_t>( + out, sum, count, false, 0, 0); + return out; + }; + + for (size_t i = 0; i < sums.size(); ++i) { + if (avgCol->view().nullable()) { + EXPECT_TRUE(isValidAt(avgMask, i)) << "row " << i; + } + EXPECT_EQ(outAvg[i], avgUnscaled(sums[i], counts[i])) << "row " << i; + } +} + +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector sums = {80, -40, 1000}; + std::vector counts = {2, 4, 10}; + + auto sumCol = makeDecimalColumn(sums, 2, nullptr, stream); + auto countCol = makeInt64Column(counts, nullptr, stream); + ASSERT_FALSE(sumCol->nullable()); + ASSERT_FALSE(countCol->nullable()); + + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + + EXPECT_EQ(avgCol->view().null_count(), 0); + auto avgMask = copyNullMask(avgCol->view(), stream); + auto outAvg = copyColumnData(avgCol->view(), stream); + + auto avgUnscaled = [](int128_t sum, int64_t count) { + __int128_t out = 0; + facebook::velox::DecimalUtil:: + divideWithRoundUp<__int128_t, __int128_t, int64_t>( + out, sum, count, false, 0, 0); + return static_cast(out); + }; + + for (size_t i = 0; i < sums.size(); ++i) { + if (avgCol->view().nullable()) { + EXPECT_TRUE(isValidAt(avgMask, i)) << "row " << i; + } + EXPECT_EQ(outAvg[i], avgUnscaled(sums[i], counts[i])) << "row " << i; + } +} + +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { + auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); + std::vector<__int128_t> sums = { + static_cast<__int128_t>(600), + static_cast<__int128_t>(-99), + }; + std::vector counts = {3, 9}; + + auto sumCol = makeDecimalColumn<__int128_t>(sums, 4, nullptr, stream); + auto countCol = makeInt64Column(counts, nullptr, stream); + ASSERT_FALSE(sumCol->nullable()); + ASSERT_FALSE(countCol->nullable()); + + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + + EXPECT_EQ(avgCol->view().null_count(), 0); + auto avgMask = copyNullMask(avgCol->view(), stream); + auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); + + auto avgUnscaled = [](int128_t sum, int64_t count) { + __int128_t out = 0; + facebook::velox::DecimalUtil:: + divideWithRoundUp<__int128_t, __int128_t, int64_t>( + out, sum, count, false, 0, 0); + return out; + }; + + for (size_t i = 0; i < sums.size(); ++i) { + if (avgCol->view().nullable()) { + EXPECT_TRUE(isValidAt(avgMask, i)) << "row " << i; + } + EXPECT_EQ(outAvg[i], avgUnscaled(sums[i], counts[i])) << "row " << i; + } +} + TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { auto stream = cudf::get_default_stream(); auto mr = cudf::get_current_device_resource_ref(); From 67f3727e875b70e9448d78390c315f90bafebc17 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 11:23:37 -0700 Subject: [PATCH 27/64] Add comments about scale in VARBINARY case, per @karthikeyann --- velox/experimental/cudf/exec/CudfGroupby.cpp | 1 + velox/experimental/cudf/exec/CudfReduce.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index af120734142..333419b98ad 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -139,6 +139,7 @@ void addDecimalIntermediateSumCountRequests( std::unique_ptr& decodedSum, std::unique_ptr& decodedCount) { validateIntermediateColumnType(tbl.column(inputIndex)); + // resultType here could be DECIMAL or VARBINARY auto scale = resultType->isDecimal() ? getDecimalPrecisionScale(*resultType).second : 0; diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 0f0a55b4c37..c2a7a884115 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -374,6 +374,7 @@ std::unique_ptr reduceIntermediateDecimalFromSerializedColumn( TypePtr const& outputType, rmm::cuda_stream_view stream) { validateIntermediateColumnType(inputCol); + // outputType here could be DECIMAL or VARBINARY auto scale = outputType->isDecimal() ? getDecimalPrecisionScale(*outputType).second : 0; From 25f06d08713260d1654f59b7aad11703f2c33f68 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 12:59:14 -0700 Subject: [PATCH 28/64] Remove test that relies on unimplemented functionality, per @karthikeyann --- .../cudf/tests/DecimalAggregationTest.cpp | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index 644e89766ab..09d9b267c3a 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -241,38 +241,6 @@ class CudfDecimalTest : public exec::test::OperatorTestBase { } }; -TEST_F(CudfDecimalTest, DISABLED_decimalAvgAndSumTimesDouble) { - auto rowType = ROW({ - {"l_quantity", DECIMAL(15, 2)}, - }); - - // Values chosen to keep the AVG and SUM exact in double. - auto input = makeRowVector( - {"l_quantity"}, - {makeFlatVector( - {125, 250, 375, 400}, // 1.25, 2.50, 3.75, 4.00 - DECIMAL(15, 2))}); - - std::vector vectors = {input}; - createDuckDbTable(vectors); - - // Force CPU-only path to validate this fails without cuDF involvement. - unregisterCudf(); - - auto plan = exec::test::PlanBuilder() - .values(vectors) - .project({"l_quantity * 2.0 AS qty2"}) - .singleAggregation( - {}, {"avg(qty2) AS avg_qty", "sum(qty2) AS sum_qty"}) - .planNode(); - - facebook::velox::exec::test::AssertQueryBuilder(plan, duckDbQueryRunner_) - .assertResults( - "SELECT avg(l_quantity * 2.0) AS avg_qty, " - "sum(l_quantity * 2.0) AS sum_qty " - "FROM tmp"); -} - TEST_F(CudfDecimalTest, decimalAvgDecimalInput) { auto rowType = ROW({ {"d", DECIMAL(12, 2)}, From 1a421ec5a4be2e220e55e8d19f02a657de0326b4 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 13:14:05 -0700 Subject: [PATCH 29/64] Add failure values, per @majetideepak --- .../experimental/cudf/exec/DecimalAggregationKernels.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index e881ebf1d15..408c2ba9cb7 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -149,7 +149,9 @@ std::unique_ptr serializeDecimalSumState( // VeloxRuntimeError for this guard. VELOX_CHECK( !useLargeOffsets || cudf::strings::is_large_strings_enabled(), - "Size of output exceeds the column size limit"); + "Size of output ({}) exceeds the column size limit ({})", + charsBytes, + threshold); auto const offsetsType = useLargeOffsets ? cudf::type_id::INT64 : cudf::type_id::INT32; @@ -177,9 +179,8 @@ std::unique_ptr serializeDecimalSumState( : static_cast(offsetsView.data()); const auto sumType = sumCol.type().id(); VELOX_CHECK( - sumType == cudf::type_id::DECIMAL64 || - sumType == cudf::type_id::DECIMAL128, - "Unsupported decimal sum column type"); + sumType == cudf::type_id::DECIMAL64 || sumType == cudf::type_id::DECIMAL128, + "Unsupported decimal sum column type ({})", static_cast(sumType)); const void* sumPtr = sumType == cudf::type_id::DECIMAL64 ? static_cast(sumCol.data()) : static_cast(sumCol.data<__int128_t>()); From c3eb088962cf763ac3e735f09e765f3f72f6cfba Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 13:15:28 -0700 Subject: [PATCH 30/64] Format --- .../experimental/cudf/exec/DecimalAggregationCommon.cpp | 3 ++- .../experimental/cudf/exec/DecimalAggregationKernels.cpp | 6 ++++-- .../cudf/exec/DecimalAggregationKernelsGpu.cu | 9 ++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp index 72e96e34a96..adc1e6b4aea 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -31,7 +31,8 @@ void validateIntermediateColumnType(cudf::column_view const& column) { VELOX_CHECK_EQ( colType, static_cast(cudf::type_id::STRING), - "Expected serialized decimal aggregation state: Velox VARBINARY represented as cuDF STRING (got type {})", colType); + "Expected serialized decimal aggregation state: Velox VARBINARY represented as cuDF STRING (got type {})", + colType); } std::unique_ptr castCountColumnToInt64( diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index 408c2ba9cb7..efadafaa079 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -179,8 +179,10 @@ std::unique_ptr serializeDecimalSumState( : static_cast(offsetsView.data()); const auto sumType = sumCol.type().id(); VELOX_CHECK( - sumType == cudf::type_id::DECIMAL64 || sumType == cudf::type_id::DECIMAL128, - "Unsupported decimal sum column type ({})", static_cast(sumType)); + sumType == cudf::type_id::DECIMAL64 || + sumType == cudf::type_id::DECIMAL128, + "Unsupported decimal sum column type ({})", + static_cast(sumType)); const void* sumPtr = sumType == cudf::type_id::DECIMAL64 ? static_cast(sumCol.data()) : static_cast(sumCol.data<__int128_t>()); diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu index 07bd2b39df1..afc32029f9b 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu @@ -31,10 +31,10 @@ namespace { // TODO: Handle overflow as in CPU. struct DecimalSumState { - int64_t count; // count of non-null input rows aggregated + int64_t count; // count of non-null input rows aggregated int64_t overflow; // overflow/extension field, not used by GPU - uint64_t lower; // lower 64 bits of the decimal sum - int64_t upper; // upper 64 bits of the decimal sum (signed) + uint64_t lower; // lower 64 bits of the decimal sum + int64_t upper; // upper 64 bits of the decimal sum (signed) }; static_assert(sizeof(DecimalSumState) == detail::kDecimalSumStateSize); @@ -90,8 +90,7 @@ struct UnpackStateFunctor { __device__ void operator()(int32_t idx) const { int64_t offset = static_cast(offsets[idx]); - auto* state = - reinterpret_cast(chars + offset); + auto* state = reinterpret_cast(chars + offset); counts[idx] = state->count; sums[idx] = (static_cast<__int128_t>(state->upper) << 64) | state->lower; } From cc456448aa2c674cf66145cdea83264755fb7e3f Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 14 May 2026 14:26:56 -0700 Subject: [PATCH 31/64] More error values, per @majetideepak --- .../cudf/exec/DecimalAggregationKernels.cpp | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index efadafaa079..f1f131125af 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -35,7 +35,8 @@ DecimalSumStateColumns deserializeDecimalSumState( rmm::device_async_resource_ref mr) { VELOX_CHECK( stateCol.type().id() == cudf::type_id::STRING, - "Decimal sum state requires STRING/VARBINARY column"); + "Decimal sum state requires STRING/VARBINARY column (type is {})", + static_cast(stateCol.type().id())); auto numRows = stateCol.size(); if (numRows == 0) { DecimalSumStateColumns empty; @@ -95,7 +96,8 @@ DecimalSumStateColumns deserializeDecimalSumState( const bool offsets64 = (offsetsType == cudf::type_id::INT64); VELOX_CHECK( offsets64 || offsetsType == cudf::type_id::INT32, - "Decimal sum state requires INT32 or INT64 offsets"); + "Decimal sum state requires INT32 or INT64 offsets (offset type is {})", + static_cast(offsetsType)); detail::unpackDecimalSumState( offsets64, offsets64 ? static_cast(offsetsView.data()) @@ -128,16 +130,20 @@ std::unique_ptr serializeDecimalSumState( rmm::device_async_resource_ref mr) { VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, - "Decimal sum state requires INT64 count column"); + "Decimal sum state requires INT64 count column (type is {})", + static_cast(countCol.type().id())); auto numRows = sumCol.size(); VELOX_CHECK_EQ( numRows, countCol.size(), - "Decimal sum state requires sum and count to be same size"); + "Decimal sum state requires sum and count to be same size (sum size is {}, count size is {})", + sumCol.size(), + countCol.size()); VELOX_CHECK_LE( numRows, static_cast(std::numeric_limits::max()), - "Too many rows to serialize decimal sum state"); + "Too many rows to serialize decimal sum state (row count is {})", + numRows); auto const rowCount = static_cast(numRows); @@ -181,7 +187,7 @@ std::unique_ptr serializeDecimalSumState( VELOX_CHECK( sumType == cudf::type_id::DECIMAL64 || sumType == cudf::type_id::DECIMAL128, - "Unsupported decimal sum column type ({})", + "Unsupported decimal sum column type (type is {})", static_cast(sumType)); const void* sumPtr = sumType == cudf::type_id::DECIMAL64 ? static_cast(sumCol.data()) @@ -214,15 +220,19 @@ std::unique_ptr computeDecimalAverage( rmm::device_async_resource_ref mr) { VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, - "Decimal average requires INT64 count column"); + "Decimal average requires INT64 count column (type is {})", + static_cast(countCol.type().id())); VELOX_CHECK( sumCol.type().id() == cudf::type_id::DECIMAL64 || sumCol.type().id() == cudf::type_id::DECIMAL128, - "Decimal average requires DECIMAL64 or DECIMAL128 sum column"); + "Decimal average requires DECIMAL64 or DECIMAL128 sum column (type is {})", + static_cast(sumCol.type().id())); VELOX_CHECK_EQ( sumCol.size(), countCol.size(), - "Decimal average requires sum and count to be same size"); + "Decimal average requires sum and count to be same size (sum size is {}, count size is {})", + sumCol.size(), + countCol.size()); auto numRows = sumCol.size(); auto out = cudf::make_fixed_width_column( From 30861b11e69725f618212f08cc5d2aac77160655 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Wed, 20 May 2026 10:34:38 -0700 Subject: [PATCH 32/64] Alternative refactor, remove MR plumbing and pass get_output_mr() or get_temp_mr() explicitly at all leaf sites --- velox/experimental/cudf/exec/CudfGroupby.cpp | 6 +- velox/experimental/cudf/exec/CudfReduce.cpp | 12 ++-- .../cudf/exec/DecimalAggregationCommon.cpp | 6 +- .../cudf/exec/DecimalAggregationKernels.cpp | 49 +++++++++------ .../cudf/exec/DecimalAggregationKernels.h | 9 +-- .../cudf/tests/DecimalAggregationTest.cpp | 61 ++++++------------- 6 files changed, 62 insertions(+), 81 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 333419b98ad..5cd5e5ddf8d 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -110,8 +110,8 @@ void addDecimalSumCountRequestsAfterDecode( uint32_t& countIdx, std::unique_ptr& decodedSum, std::unique_ptr& decodedCount) { - auto sumAndCount = cudf_velox::deserializeDecimalSumState( - encodedColumn, scale, stream, cudf_velox::get_output_mr()); + auto sumAndCount = + cudf_velox::deserializeDecimalSumState(encodedColumn, scale, stream); decodedSum.swap(sumAndCount.sum); decodedCount.swap(sumAndCount.count); @@ -190,7 +190,7 @@ void addDecimalFinalSumOnlyRequest( auto& request = requests.emplace_back(); sumIdx = requests.size() - 1; auto sumAndCount = cudf_velox::deserializeDecimalSumState( - tbl.column(inputIndex), scale, stream, cudf_velox::get_output_mr()); + tbl.column(inputIndex), scale, stream); decodedSum.swap(sumAndCount.sum); request.values = decodedSum->view(); request.aggregations.push_back( diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index c2a7a884115..9d4825ecd5d 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -279,8 +279,8 @@ std::unique_ptr intermediateDecimalMergeSerializedString( int32_t scale, rmm::cuda_stream_view stream) { auto const sumAgg = cudf::make_sum_aggregation(); - auto sumAndCount = cudf_velox::deserializeDecimalSumState( - inputCol, scale, stream, get_output_mr()); + auto sumAndCount = + cudf_velox::deserializeDecimalSumState(inputCol, scale, stream); auto sumScalar = cudf::reduce( sumAndCount.sum->view(), *sumAgg, @@ -307,8 +307,8 @@ std::unique_ptr finalDecimalAvgFromSerializedString( TypePtr const& resultType, rmm::cuda_stream_view stream) { auto const sumAgg = cudf::make_sum_aggregation(); - auto sumAndCount = cudf_velox::deserializeDecimalSumState( - inputCol, scale, stream, get_output_mr()); + auto sumAndCount = + cudf_velox::deserializeDecimalSumState(inputCol, scale, stream); auto sumScalar = cudf::reduce( sumAndCount.sum->view(), *sumAgg, @@ -387,8 +387,8 @@ std::unique_ptr reduceFinalDecimalSumFromSerializedColumn( rmm::cuda_stream_view stream) { validateIntermediateColumnType(inputCol); auto scale = getDecimalPrecisionScale(*outputType).second; - auto sumAndCount = cudf_velox::deserializeDecimalSumState( - inputCol, scale, stream, get_output_mr()); + auto sumAndCount = + cudf_velox::deserializeDecimalSumState(inputCol, scale, stream); return singleOrRawDecimalSumWithCast( sumAndCount.sum->view(), outputType, stream); } diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp index adc1e6b4aea..019c17f4cff 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -50,8 +50,7 @@ std::unique_ptr serializeDecimalPartialOrIntermediateState( std::unique_ptr count, rmm::cuda_stream_view stream) { count = castCountColumnToInt64(std::move(count), stream); - return serializeDecimalSumState( - sum->view(), count->view(), stream, get_output_mr()); + return serializeDecimalSumState(sum->view(), count->view(), stream); } std::unique_ptr finalizeDecimalAverage( @@ -60,8 +59,7 @@ std::unique_ptr finalizeDecimalAverage( const TypePtr& resultType, rmm::cuda_stream_view stream) { count = castCountColumnToInt64(std::move(count), stream); - auto avgCol = computeDecimalAverage( - sum->view(), count->view(), stream, get_output_mr()); + auto avgCol = computeDecimalAverage(sum->view(), count->view(), stream); auto const cudfOutType = veloxToCudfDataType(resultType); if (avgCol->type() != cudfOutType) { avgCol = cudf::cast(avgCol->view(), cudfOutType, stream, get_output_mr()); diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index f1f131125af..fada76da009 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -16,6 +16,7 @@ #include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h" +#include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/common/base/Exceptions.h" @@ -31,8 +32,7 @@ namespace facebook::velox::cudf_velox { DecimalSumStateColumns deserializeDecimalSumState( const cudf::column_view& stateCol, int32_t scale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { + rmm::cuda_stream_view stream) { VELOX_CHECK( stateCol.type().id() == cudf::type_id::STRING, "Decimal sum state requires STRING/VARBINARY column (type is {})", @@ -44,12 +44,14 @@ DecimalSumStateColumns deserializeDecimalSumState( cudf::data_type{cudf::type_id::DECIMAL128, -scale}, 0, cudf::mask_state::UNALLOCATED, - stream); + stream, + get_output_mr()); empty.count = cudf::make_fixed_width_column( cudf::data_type{cudf::type_id::INT64}, 0, cudf::mask_state::UNALLOCATED, - stream); + stream, + get_output_mr()); return empty; } @@ -61,12 +63,14 @@ DecimalSumStateColumns deserializeDecimalSumState( cudf::data_type{cudf::type_id::DECIMAL128, -scale}, numRows, cudf::mask_state::ALL_NULL, - stream); + stream, + get_output_mr()); allNull.count = cudf::make_fixed_width_column( cudf::data_type{cudf::type_id::INT64}, numRows, cudf::mask_state::ALL_NULL, - stream); + stream, + get_output_mr()); return allNull; } @@ -81,12 +85,14 @@ DecimalSumStateColumns deserializeDecimalSumState( cudf::data_type{cudf::type_id::DECIMAL128, -scale}, numRows, cudf::mask_state::UNALLOCATED, - stream); + stream, + get_output_mr()); auto countCol = cudf::make_fixed_width_column( cudf::data_type{cudf::type_id::INT64}, numRows, cudf::mask_state::UNALLOCATED, - stream); + stream, + get_output_mr()); auto sumView = sumCol->mutable_view(); auto countView = countCol->mutable_view(); @@ -110,10 +116,10 @@ DecimalSumStateColumns deserializeDecimalSumState( } if (stateCol.nullable()) { - auto nullMask = cudf::copy_bitmask(stateCol, stream, mr); + auto nullMask = cudf::copy_bitmask(stateCol, stream, get_output_mr()); auto nullCount = stateCol.null_count(); sumCol->set_null_mask(std::move(nullMask), nullCount); - auto countMask = cudf::copy_bitmask(stateCol, stream, mr); + auto countMask = cudf::copy_bitmask(stateCol, stream, get_output_mr()); countCol->set_null_mask(std::move(countMask), nullCount); } @@ -126,8 +132,7 @@ DecimalSumStateColumns deserializeDecimalSumState( std::unique_ptr serializeDecimalSumState( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { + rmm::cuda_stream_view stream) { VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, "Decimal sum state requires INT64 count column (type is {})", @@ -165,11 +170,14 @@ std::unique_ptr serializeDecimalSumState( cudf::data_type{offsetsType}, numRows + 1, cudf::mask_state::UNALLOCATED, - stream); + stream, + get_output_mr()); auto offsetsView = offsetsCol->mutable_view(); rmm::device_buffer charsBuf( - static_cast(numRows) * detail::kDecimalSumStateSize, stream); + static_cast(numRows) * detail::kDecimalSumStateSize, + stream, + get_output_mr()); detail::fillOffsetsForDecimalSumState( useLargeOffsets, @@ -204,7 +212,7 @@ std::unique_ptr serializeDecimalSumState( } auto [nullMask, nullCount] = - detail::buildStateValidityMask(sumCol, countCol, stream, mr); + detail::buildStateValidityMask(sumCol, countCol, stream, get_output_mr()); return cudf::make_strings_column( static_cast(numRows), std::move(offsetsCol), @@ -216,8 +224,7 @@ std::unique_ptr serializeDecimalSumState( std::unique_ptr computeDecimalAverage( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { + rmm::cuda_stream_view stream) { VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, "Decimal average requires INT64 count column (type is {})", @@ -236,7 +243,11 @@ std::unique_ptr computeDecimalAverage( auto numRows = sumCol.size(); auto out = cudf::make_fixed_width_column( - sumCol.type(), numRows, cudf::mask_state::UNALLOCATED, stream); + sumCol.type(), + numRows, + cudf::mask_state::UNALLOCATED, + stream, + get_output_mr()); if (numRows > 0) { auto const rowCount = static_cast(numRows); @@ -252,7 +263,7 @@ std::unique_ptr computeDecimalAverage( } auto [nullMask, nullCount] = - detail::buildStateValidityMask(sumCol, countCol, stream, mr); + detail::buildStateValidityMask(sumCol, countCol, stream, get_output_mr()); if (nullCount > 0) { out->set_null_mask(std::move(nullMask), nullCount); } diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.h b/velox/experimental/cudf/exec/DecimalAggregationKernels.h index 3a0c6eeddba..2a31c6fc8ee 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.h +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.h @@ -38,8 +38,7 @@ struct DecimalSumStateColumns { DecimalSumStateColumns deserializeDecimalSumState( const cudf::column_view& stateCol, int32_t scale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr); + rmm::cuda_stream_view stream); // Encodes partial decimal SUM state (DECIMAL64 or DECIMAL128 sums plus // INT64 counts) into a single STRING column (later converted to Velox @@ -50,8 +49,7 @@ DecimalSumStateColumns deserializeDecimalSumState( std::unique_ptr serializeDecimalSumState( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr); + rmm::cuda_stream_view stream); // Finalizes AVG from intermediate SUM state: divides each sum by its count // on device with decimal-specific rounding (see averageRoundDecimalSum), @@ -61,7 +59,6 @@ std::unique_ptr serializeDecimalSumState( std::unique_ptr computeDecimalAverage( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr); + rmm::cuda_stream_view stream); } // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index 09d9b267c3a..4688434107c 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -955,7 +955,6 @@ TEST_F(CudfDecimalTest, decimalSumGlobalIntermediateVarbinaryAllNulls) { TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, -200, 300}; std::vector counts = {1, 2, 0}; std::vector sumValid = {true, false, true}; @@ -964,9 +963,8 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); - auto sumAndCount = - deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 2, stream); auto stateMask = copyNullMask(stateCol->view(), stream); auto sumMask = copyNullMask(sumAndCount.sum->view(), stream); EXPECT_EQ(stateMask, sumMask); @@ -983,7 +981,6 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal128) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(123450), static_cast<__int128_t>(-25000), @@ -996,9 +993,8 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal128) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); - auto sumAndCount = - deserializeDecimalSumState(stateCol->view(), 3, stream, mr); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 3, stream); auto stateMask = copyNullMask(stateCol->view(), stream); auto sumMask = copyNullMask(sumAndCount.sum->view(), stream); EXPECT_EQ(stateMask, sumMask); @@ -1015,7 +1011,6 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal128) { TEST_F(CudfDecimalTest, decimalDeserializeSumStateAllNull) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); constexpr cudf::size_type numRows = 4; auto offsetsCol = cudf::make_fixed_width_column( @@ -1042,8 +1037,7 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateAllNull) { nullCount, std::move(nullMask)); - auto sumAndCount = - deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 2, stream); auto outSumView = sumAndCount.sum->view(); auto outCountView = sumAndCount.count->view(); @@ -1062,7 +1056,6 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateAllNull) { TEST_F(CudfDecimalTest, decimalSerializeSumStateUsesInt64OffsetsWhenEnabled) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); ScopedEnvVar enableLargeStrings("LIBCUDF_LARGE_STRINGS_ENABLED", "1"); ScopedEnvVar threshold("LIBCUDF_LARGE_STRINGS_THRESHOLD", "1"); @@ -1072,7 +1065,7 @@ TEST_F(CudfDecimalTest, decimalSerializeSumStateUsesInt64OffsetsWhenEnabled) { auto sumCol = makeDecimalColumn(sums, 2, nullptr, stream); auto countCol = makeInt64Column(counts, nullptr, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream); cudf::strings_column_view strings(stateCol->view()); EXPECT_EQ(strings.offsets().type().id(), cudf::type_id::INT64); @@ -1080,7 +1073,6 @@ TEST_F(CudfDecimalTest, decimalSerializeSumStateUsesInt64OffsetsWhenEnabled) { TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); ScopedEnvVar enableLargeStrings("LIBCUDF_LARGE_STRINGS_ENABLED", "1"); ScopedEnvVar threshold("LIBCUDF_LARGE_STRINGS_THRESHOLD", "1"); @@ -1092,13 +1084,12 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream); cudf::strings_column_view strings(stateCol->view()); EXPECT_EQ(strings.offsets().type().id(), cudf::type_id::INT64); - auto sumAndCount = - deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 2, stream); auto outSumView = sumAndCount.sum->view(); auto outCountView = sumAndCount.count->view(); auto outSum = copyColumnData<__int128_t>(outSumView, stream); @@ -1123,7 +1114,6 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, 105, 250, -125}; std::vector counts = {4, 2, 0, 2}; std::vector sumValid = {true, true, true, true}; @@ -1131,8 +1121,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = - computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); auto avgMask = copyNullMask(avgCol->view(), stream); auto outAvg = copyColumnData(avgCol->view(), stream); @@ -1156,7 +1145,6 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(123450), static_cast<__int128_t>(-25000), @@ -1168,8 +1156,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = - computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); auto avgMask = copyNullMask(avgCol->view(), stream); auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); @@ -1193,7 +1180,6 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, 200, -150}; std::vector counts = {4, 5, 3}; std::vector sumValid = {true, true, true}; @@ -1201,8 +1187,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = - computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1226,7 +1211,6 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(90000), static_cast<__int128_t>(-5000), @@ -1238,8 +1222,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = - computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1263,7 +1246,6 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {80, -40, 1000}; std::vector counts = {2, 4, 10}; @@ -1272,8 +1254,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { ASSERT_FALSE(sumCol->nullable()); ASSERT_FALSE(countCol->nullable()); - auto avgCol = - computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1297,7 +1278,6 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(600), static_cast<__int128_t>(-99), @@ -1309,8 +1289,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { ASSERT_FALSE(sumCol->nullable()); ASSERT_FALSE(countCol->nullable()); - auto avgCol = - computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1334,7 +1313,6 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, -200, 300, 400}; std::vector counts = {1, 0, 2, 3}; std::vector sumValid = {true, true, false, true}; @@ -1343,11 +1321,10 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream); auto stateMask = copyNullMask(stateCol->view(), stream); - auto sumAndCount = - deserializeDecimalSumState(stateCol->view(), 2, stream, mr); + auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 2, stream); auto outSumView = sumAndCount.sum->view(); auto outCountView = sumAndCount.count->view(); auto outSum = copyColumnData<__int128_t>(outSumView, stream); @@ -1370,7 +1347,6 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal128) { auto stream = cudf::get_default_stream(); - auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(123450), static_cast<__int128_t>(-25000), @@ -1383,11 +1359,10 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal128) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream); auto stateMask = copyNullMask(stateCol->view(), stream); - auto sumAndCount = - deserializeDecimalSumState(stateCol->view(), 3, stream, mr); + auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 3, stream); auto outSumView = sumAndCount.sum->view(); auto outCountView = sumAndCount.count->view(); auto outSum = copyColumnData<__int128_t>(outSumView, stream); From 85c762e92a54b04ab2dff8f32f543452e8b66950 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Mon, 1 Jun 2026 15:23:51 -0700 Subject: [PATCH 33/64] Fix UB signed overflow issue --- .../cudf/exec/DecimalAggregationKernelsGpu.cu | 11 +++--- .../cudf/tests/DecimalAggregationTest.cpp | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu index afc32029f9b..64e6e1644ec 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -109,10 +110,12 @@ struct AvgRoundFunctor { return; } auto sum = sums[idx]; - SumT absSum = sum < 0 ? -sum : sum; - SumT half = static_cast(count / 2); - SumT rounded = (absSum + half) / static_cast(count); - out[idx] = sum < 0 ? -rounded : rounded; + using U = cuda::std::make_unsigned_t; + U absSum = sum < 0 ? -static_cast(sum) : static_cast(sum); + U half = static_cast(count / 2); + U rounded = (absSum + half) / static_cast(count); + // Use `U{0} - rounded` below to avoid signed overflow + out[idx] = static_cast(sum < 0 ? U{0} - rounded : rounded); } }; diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index 4688434107c..361ecd2cb8a 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -1178,6 +1179,41 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { } } +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64MostNegativeSum) { + // Negating INT64_MIN in the signed type is overflow UB; the magnitude and + // sign must be handled in the unsigned domain. avg of one INT64_MIN is + // itself. + auto stream = cudf::get_default_stream(); + constexpr int64_t kMin = std::numeric_limits::min(); + std::vector sums = {kMin}; + std::vector counts = {1}; + std::vector valid = {true}; + + auto sumCol = makeDecimalColumn(sums, 0, &valid, stream); + auto countCol = makeInt64Column(counts, &valid, stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + + auto outAvg = copyColumnData(avgCol->view(), stream); + EXPECT_EQ(outAvg[0], kMin); +} + +TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128MostNegativeSum) { + // Same regression at the __int128 boundary. avg of one -2^127 is itself. + auto stream = cudf::get_default_stream(); + const __int128_t kMin = + static_cast<__int128_t>(static_cast(1) << 127); + std::vector<__int128_t> sums = {kMin}; + std::vector counts = {1}; + std::vector valid = {true}; + + auto sumCol = makeDecimalColumn<__int128_t>(sums, 0, &valid, stream); + auto countCol = makeInt64Column(counts, &valid, stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + + auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); + EXPECT_EQ(outAvg[0], kMin); +} + TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { auto stream = cudf::get_default_stream(); std::vector sums = {100, 200, -150}; From 8252ef25c90c58d8c577a13eddc1015f92002b58 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Mon, 1 Jun 2026 16:49:22 -0700 Subject: [PATCH 34/64] Ensure 64bit decimals are upcast to 128bit before aggregation to prevent overflow/wrapping --- velox/experimental/cudf/exec/CudfGroupby.cpp | 23 +++- velox/experimental/cudf/exec/CudfReduce.cpp | 5 + .../cudf/exec/DecimalAggregationCommon.cpp | 16 +++ .../cudf/exec/DecimalAggregationCommon.h | 9 ++ .../cudf/tests/DecimalAggregationTest.cpp | 120 ++++++++++++++++++ 5 files changed, 169 insertions(+), 4 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 5cd5e5ddf8d..9e86910421f 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -41,6 +41,7 @@ namespace { using namespace facebook::velox; +using cudf_velox::castDecimal64InputToDecimal128; using cudf_velox::CountInputKind; using cudf_velox::finalizeDecimalAverage; using cudf_velox::get_output_mr; @@ -202,10 +203,14 @@ void addDecimalRawPartialSingleSumRequest( uint32_t inputIndex, std::vector& requests, bool includeCountAggregation, - uint32_t& sumIdx) { + rmm::cuda_stream_view stream, + uint32_t& sumIdx, + std::unique_ptr& castedInput) { + auto inputView = castDecimal64InputToDecimal128( + tbl.column(inputIndex), castedInput, stream); auto& request = requests.emplace_back(); sumIdx = requests.size() - 1; - request.values = tbl.column(inputIndex); + request.values = inputView; request.aggregations.push_back( cudf::make_sum_aggregation()); if (includeCountAggregation) { @@ -247,7 +252,9 @@ struct GroupbyDecimalSumAggregator : GroupbyAggregator { inputIndex, requests, step == core::AggregationNode::Step::kPartial, - sumIdx_); + stream, + sumIdx_, + castedInput_); } } @@ -277,6 +284,9 @@ struct GroupbyDecimalSumAggregator : GroupbyAggregator { uint32_t countIdx_{0}; std::unique_ptr decodedSum_; std::unique_ptr decodedCount_; + // Holds the DECIMAL64->DECIMAL128 cast of raw input (kPartial/kSingle), kept + // alive while the groupby request references its view. + std::unique_ptr castedInput_; }; struct GroupbyDecimalAvgAggregator : GroupbyAggregator { @@ -320,7 +330,9 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { requests, step == core::AggregationNode::Step::kPartial || step == core::AggregationNode::Step::kSingle, - sumIdx_); + stream, + sumIdx_, + castedInput_); } } @@ -360,6 +372,9 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { uint32_t countIdx_{0}; std::unique_ptr decodedSum_; std::unique_ptr decodedCount_; + // Holds the DECIMAL64->DECIMAL128 cast of raw input (kPartial/kSingle), kept + // alive while the groupby request references its view. + std::unique_ptr castedInput_; }; struct GroupbyCountAggregator : GroupbyAggregator { diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 9d4825ecd5d..cdbe28ee6fd 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -42,6 +42,7 @@ namespace { using namespace facebook::velox; +using facebook::velox::cudf_velox::castDecimal64InputToDecimal128; using facebook::velox::cudf_velox::CountInputKind; using facebook::velox::cudf_velox::finalizeDecimalAverage; using facebook::velox::cudf_velox::get_output_mr; @@ -255,6 +256,8 @@ struct ReduceMeanAggregator : ReduceAggregator { std::unique_ptr partialDecimalSumCountToSerializedString( cudf::column_view inputCol, rmm::cuda_stream_view stream) { + std::unique_ptr castedInput; + inputCol = castDecimal64InputToDecimal128(inputCol, castedInput, stream); auto const sumAgg = cudf::make_sum_aggregation(); auto sumScalar = cudf::reduce(inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); @@ -333,6 +336,8 @@ std::unique_ptr singleDecimalAvgFromRawColumn( cudf::column_view inputCol, TypePtr const& resultType, rmm::cuda_stream_view stream) { + std::unique_ptr castedInput; + inputCol = castDecimal64InputToDecimal128(inputCol, castedInput, stream); auto const sumAgg = cudf::make_sum_aggregation(); auto sumScalar = cudf::reduce(inputCol, *sumAgg, inputCol.type(), stream, get_temp_mr()); diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp index 019c17f4cff..080624f10d2 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "velox/experimental/cudf/CudfNoDefaults.h" #include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/GpuResources.h" @@ -35,6 +36,21 @@ void validateIntermediateColumnType(cudf::column_view const& column) { colType); } +cudf::column_view castDecimal64InputToDecimal128( + cudf::column_view inputCol, + std::unique_ptr& holder, + rmm::cuda_stream_view stream) { + if (inputCol.type().id() != cudf::type_id::DECIMAL64) { + return inputCol; + } + holder = cudf::cast( + inputCol, + cudf::data_type{cudf::type_id::DECIMAL128, inputCol.type().scale()}, + stream, + get_temp_mr()); + return holder->view(); +} + std::unique_ptr castCountColumnToInt64( std::unique_ptr count, rmm::cuda_stream_view stream) { diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.h b/velox/experimental/cudf/exec/DecimalAggregationCommon.h index 20dec2e6449..5273be53caa 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.h +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.h @@ -31,6 +31,15 @@ namespace facebook::velox::cudf_velox { // sum/count payloads (see serializeDecimalSumState). void validateIntermediateColumnType(cudf::column_view const& column); +// Casts a DECIMAL64 column up to DECIMAL128 (scale preserved) so a subsequent +// SUM accumulates in 128 bits instead of wrapping. Allocates the casted column +// from the temporary memory resource into holder and returns its view. +// Lifetime stays valid only while holder is alive. +cudf::column_view castDecimal64InputToDecimal128( + cudf::column_view inputCol, + std::unique_ptr& holder, + rmm::cuda_stream_view stream); + // Ensures the partial-row count column is INT64, casting with the default GPU // output memory resource when the incoming type differs. std::unique_ptr castCountColumnToInt64( diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index 361ecd2cb8a..d768f4c8cfb 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -475,6 +475,64 @@ TEST_F(CudfDecimalTest, decimalAvgGlobalSingleRounds) { facebook::velox::test::assertEqualVectors(expected, result); } +TEST_F(CudfDecimalTest, decimalAvgGlobalSingleDecimal64Overflow) { + // 12 values of 9e17 (DECIMAL(18,0)) sum to 1.08e19, past 2^63. The sum must + // accumulate in 128 bits or a DECIMAL64 accumulator wraps; avg is 9e17. + constexpr int64_t kBig = 900'000'000'000'000'000; + constexpr int kNumRows = 12; + std::vector values(kNumRows, kBig); + + auto input = + makeRowVector({"d"}, {makeFlatVector(values, DECIMAL(18, 0))}); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({}, {"avg(d) AS a"}) + .planNode(); + + auto expected = + makeRowVector({"a"}, {makeFlatVector({kBig}, DECIMAL(18, 0))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalAvgGroupbySingleDecimal64Overflow) { + // Same overflow within a single group, exercising the groupby raw sum path. + constexpr int64_t kBig = 900'000'000'000'000'000; + constexpr int kNumRows = 12; + std::vector keys(kNumRows, 1); + std::vector values(kNumRows, kBig); + + auto input = makeRowVector( + {"k", "d"}, + { + makeFlatVector(keys), + makeFlatVector(values, DECIMAL(18, 0)), + }); + + std::vector vectors = {input}; + + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({"k"}, {"avg(d) AS a"}) + .planNode(); + + auto expected = makeRowVector( + {"k", "a"}, + { + makeFlatVector({1}), + makeFlatVector({kBig}, DECIMAL(18, 0)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + TEST_F(CudfDecimalTest, decimalAvgGlobalSingleAllNulls) { auto rowType = ROW({ {"d", DECIMAL(12, 2)}, @@ -812,6 +870,68 @@ TEST_F(CudfDecimalTest, decimalSumGlobalSingle) { .assertResults("SELECT sum(d) AS s FROM tmp"); } +TEST_F(CudfDecimalTest, decimalSumGroupbySingleDecimal64Overflow) { + // One group of 12 values of 9e17 (DECIMAL(18,0)) sums to 1.08e19, past 2^63. + // sum(decimal(18,0)) -> decimal(38,0), computed in 128 bits, no wrap. + constexpr int64_t kBig = 900'000'000'000'000'000; + constexpr int kNumRows = 12; + std::vector keys(kNumRows, 1); + std::vector values(kNumRows, kBig); + + auto input = makeRowVector( + {"k", "d"}, + { + makeFlatVector(keys), + makeFlatVector(values, DECIMAL(18, 0)), + }); + + std::vector vectors = {input}; + + const int128_t expectedSum = static_cast(kBig) * kNumRows; + auto plan = exec::test::PlanBuilder() + .values(vectors) + .singleAggregation({"k"}, {"sum(d) AS s"}) + .planNode(); + + auto expected = makeRowVector( + {"k", "s"}, + { + makeFlatVector({1}), + makeFlatVector({expectedSum}, DECIMAL(38, 0)), + }); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + +TEST_F(CudfDecimalTest, decimalSumGlobalPartialFinalDecimal64Overflow) { + // Global SUM whose total overflows DECIMAL64; exercises the partial raw sum + // (serialized to VARBINARY) and the final merge, both in 128 bits. + constexpr int64_t kBig = 900'000'000'000'000'000; + constexpr int kNumRows = 12; + std::vector values(kNumRows, kBig); + + auto input = + makeRowVector({"d"}, {makeFlatVector(values, DECIMAL(18, 0))}); + + std::vector vectors = {input}; + + const int128_t expectedSum = static_cast(kBig) * kNumRows; + auto plan = exec::test::PlanBuilder() + .values(vectors) + .partialAggregation({}, {"sum(d) AS s"}) + .finalAggregation() + .planNode(); + + auto expected = makeRowVector( + {"s"}, {makeFlatVector({expectedSum}, DECIMAL(38, 0))}); + + auto result = + facebook::velox::exec::test::AssertQueryBuilder(plan).copyResults(pool()); + facebook::velox::test::assertEqualVectors(expected, result); +} + TEST_F(CudfDecimalTest, decimalSumPartialFinalVarbinaryNullGroup) { auto rowType = ROW({ {"k", INTEGER()}, From dbae04799f28f188a86f1e30de2629877e44b786 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Mon, 1 Jun 2026 17:06:08 -0700 Subject: [PATCH 35/64] Add comment clarifying `overflow` member --- .../experimental/cudf/exec/DecimalAggregationKernelsGpu.cu | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu index 64e6e1644ec..f5aeddaa24f 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu @@ -30,10 +30,13 @@ namespace facebook::velox::cudf_velox { namespace { -// TODO: Handle overflow as in CPU. +// Mirrors the CPU LongDecimalWithOverflowState layout so serialized SUM state +// is interchangeable between CPU and GPU aggregation. +// TODO: Track int128 overflow as the CPU does (DecimalUtil::addWithOverflow); +// the `overflow` field is reserved for that and is always 0 until then. struct DecimalSumState { int64_t count; // count of non-null input rows aggregated - int64_t overflow; // overflow/extension field, not used by GPU + int64_t overflow; // net int128 carries (CPU parity); always 0 on GPU for now uint64_t lower; // lower 64 bits of the decimal sum int64_t upper; // upper 64 bits of the decimal sum (signed) }; From 04a3541eb2ae102b404f2196124b532393ebd5ec Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Mon, 1 Jun 2026 17:49:41 -0700 Subject: [PATCH 36/64] Address resource accounting (i.e. use of `temp_mr` where appropriate.) --- velox/experimental/cudf/exec/CudfGroupby.cpp | 4 +-- velox/experimental/cudf/exec/CudfReduce.cpp | 28 ++++++++++++------- .../cudf/exec/DecimalAggregationCommon.cpp | 2 +- .../cudf/exec/DecimalAggregationCommon.h | 5 ++-- .../cudf/exec/DecimalAggregationKernels.cpp | 20 +++++++------ 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 9e86910421f..cc5b6d849ec 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -1014,7 +1014,7 @@ void CudfGroupby::computePartialGroupbyStreaming(CudfVectorPtr tbl) { std::move(tablesToConcat), bufferedResultType_, partialOutputStream, - get_output_mr()); + get_temp_mr()); // Now we have to groupby again but this time with intermediate aggregators. // Keep concatenatedTable alive while we use its view. @@ -1264,7 +1264,7 @@ RowVectorPtr CudfGroupby::doGetOutput() { auto stream = cudfGlobalStreamPool().get_stream(); auto tbl = getConcatenatedTable( - std::exchange(inputs_, {}), inputType_, stream, get_output_mr()); + std::exchange(inputs_, {}), inputType_, stream, get_temp_mr()); // Release input data after synchronizing. stream.synchronize(); diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index cdbe28ee6fd..c40def3adea 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -269,10 +269,12 @@ std::unique_ptr partialDecimalSumCountToSerializedString( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); + // sumCol/countCol are consumed by the serialize/finalize call below and never + // leave the operator, so they come from the temporary memory resource. auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); return serializeDecimalPartialOrIntermediateState( std::move(sumCol), std::move(countCol), stream); } @@ -296,10 +298,12 @@ std::unique_ptr intermediateDecimalMergeSerializedString( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); + // sumCol/countCol are consumed by the serialize/finalize call below and never + // leave the operator, so they come from the temporary memory resource. auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); return serializeDecimalPartialOrIntermediateState( std::move(sumCol), std::move(countCol), stream); } @@ -324,10 +328,12 @@ std::unique_ptr finalDecimalAvgFromSerializedString( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); + // sumCol/countCol are consumed by the serialize/finalize call below and never + // leave the operator, so they come from the temporary memory resource. auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); return finalizeDecimalAverage( std::move(sumCol), std::move(countCol), resultType, stream); } @@ -349,10 +355,12 @@ std::unique_ptr singleDecimalAvgFromRawColumn( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); + // sumCol/countCol are consumed by the serialize/finalize call below and never + // leave the operator, so they come from the temporary memory resource. auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_output_mr()); + cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); return finalizeDecimalAverage( std::move(sumCol), std::move(countCol), resultType, stream); } @@ -365,7 +373,7 @@ std::unique_ptr singleOrRawDecimalSumWithCast( auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); std::unique_ptr castedInput; if (outputType->isDecimal() && inputCol.type() != cudfOutType) { - castedInput = cudf::cast(inputCol, cudfOutType, stream, get_output_mr()); + castedInput = cudf::cast(inputCol, cudfOutType, stream, get_temp_mr()); inputCol = castedInput->view(); } auto const resultScalar = @@ -868,7 +876,7 @@ RowVectorPtr CudfReduce::doGetOutput() { auto stream = cudfGlobalStreamPool().get_stream(); auto tbl = getConcatenatedTable( - std::move(inputs_), inputType_, stream, get_output_mr()); + std::move(inputs_), inputType_, stream, get_temp_mr()); // Release input data after synchronizing. stream.synchronize(); diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp index 080624f10d2..b4696588d40 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp @@ -56,7 +56,7 @@ std::unique_ptr castCountColumnToInt64( rmm::cuda_stream_view stream) { if (count->type().id() != cudf::type_id::INT64) { count = cudf::cast( - *count, cudf::data_type{cudf::type_id::INT64}, stream, get_output_mr()); + *count, cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); } return count; } diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.h b/velox/experimental/cudf/exec/DecimalAggregationCommon.h index 5273be53caa..851bd9874d6 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.h +++ b/velox/experimental/cudf/exec/DecimalAggregationCommon.h @@ -40,8 +40,9 @@ cudf::column_view castDecimal64InputToDecimal128( std::unique_ptr& holder, rmm::cuda_stream_view stream); -// Ensures the partial-row count column is INT64, casting with the default GPU -// output memory resource when the incoming type differs. +// Ensures the partial-row count column is INT64, casting with the temporary +// memory resource (the result is consumed internally, not part of operator +// output) when the incoming type differs. std::unique_ptr castCountColumnToInt64( std::unique_ptr count, rmm::cuda_stream_view stream); diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index fada76da009..8387eb7cb82 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "velox/experimental/cudf/CudfNoDefaults.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" #include "velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h" #include "velox/experimental/cudf/exec/GpuResources.h" @@ -37,6 +38,9 @@ DecimalSumStateColumns deserializeDecimalSumState( stateCol.type().id() == cudf::type_id::STRING, "Decimal sum state requires STRING/VARBINARY column (type is {})", static_cast(stateCol.type().id())); + // The decoded sum/count columns are consumed by the next groupby/reduce and + // never leave the operator and should use the temporary memory resource. + auto const mr = get_temp_mr(); auto numRows = stateCol.size(); if (numRows == 0) { DecimalSumStateColumns empty; @@ -45,13 +49,13 @@ DecimalSumStateColumns deserializeDecimalSumState( 0, cudf::mask_state::UNALLOCATED, stream, - get_output_mr()); + mr); empty.count = cudf::make_fixed_width_column( cudf::data_type{cudf::type_id::INT64}, 0, cudf::mask_state::UNALLOCATED, stream, - get_output_mr()); + mr); return empty; } @@ -64,13 +68,13 @@ DecimalSumStateColumns deserializeDecimalSumState( numRows, cudf::mask_state::ALL_NULL, stream, - get_output_mr()); + mr); allNull.count = cudf::make_fixed_width_column( cudf::data_type{cudf::type_id::INT64}, numRows, cudf::mask_state::ALL_NULL, stream, - get_output_mr()); + mr); return allNull; } @@ -86,13 +90,13 @@ DecimalSumStateColumns deserializeDecimalSumState( numRows, cudf::mask_state::UNALLOCATED, stream, - get_output_mr()); + mr); auto countCol = cudf::make_fixed_width_column( cudf::data_type{cudf::type_id::INT64}, numRows, cudf::mask_state::UNALLOCATED, stream, - get_output_mr()); + mr); auto sumView = sumCol->mutable_view(); auto countView = countCol->mutable_view(); @@ -116,10 +120,10 @@ DecimalSumStateColumns deserializeDecimalSumState( } if (stateCol.nullable()) { - auto nullMask = cudf::copy_bitmask(stateCol, stream, get_output_mr()); + auto nullMask = cudf::copy_bitmask(stateCol, stream, mr); auto nullCount = stateCol.null_count(); sumCol->set_null_mask(std::move(nullMask), nullCount); - auto countMask = cudf::copy_bitmask(stateCol, stream, get_output_mr()); + auto countMask = cudf::copy_bitmask(stateCol, stream, mr); countCol->set_null_mask(std::move(countMask), nullCount); } From 35a7ce24cc56136d17cea0ab94cbb83691d621b9 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Mon, 1 Jun 2026 22:06:57 -0700 Subject: [PATCH 37/64] Remove dead/redundant code --- velox/experimental/cudf/exec/CudfGroupby.cpp | 7 +- .../cudf/exec/DecimalAggregationKernels.cpp | 83 +++++++++---------- 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index cc5b6d849ec..932cdd66c5f 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -360,11 +360,8 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { return finalizeDecimalAverage( std::move(col), std::move(count), resultType, stream); } - auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); - if (col->type() != cudfResType) { - col = cudf::cast(*col, cudfResType, stream, cudf_velox::get_output_mr()); - } - return col; + // All four aggregation steps are handled above. + VELOX_UNREACHABLE(); } private: diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp index 8387eb7cb82..3a0e69897a1 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp @@ -79,7 +79,6 @@ DecimalSumStateColumns deserializeDecimalSumState( } cudf::strings_column_view strings(stateCol); - numRows = strings.size(); auto offsetsView = strings.offsets(); auto offsetsType = offsetsView.type().id(); @@ -101,23 +100,21 @@ DecimalSumStateColumns deserializeDecimalSumState( auto sumView = sumCol->mutable_view(); auto countView = countCol->mutable_view(); - if (numRows > 0) { - auto const rowCount = static_cast(numRows); - const bool offsets64 = (offsetsType == cudf::type_id::INT64); - VELOX_CHECK( - offsets64 || offsetsType == cudf::type_id::INT32, - "Decimal sum state requires INT32 or INT64 offsets (offset type is {})", - static_cast(offsetsType)); - detail::unpackDecimalSumState( - offsets64, - offsets64 ? static_cast(offsetsView.data()) - : static_cast(offsetsView.data()), - charsPtr, - sumView.data<__int128_t>(), - countView.data(), - rowCount, - stream); - } + // numRows is guaranteed positive here + const bool offsets64 = (offsetsType == cudf::type_id::INT64); + VELOX_CHECK( + offsets64 || offsetsType == cudf::type_id::INT32, + "Decimal sum state requires INT32 or INT64 offsets (offset type is {})", + static_cast(offsetsType)); + detail::unpackDecimalSumState( + offsets64, + offsets64 ? static_cast(offsetsView.data()) + : static_cast(offsetsView.data()), + charsPtr, + sumView.data<__int128_t>(), + countView.data(), + numRows, + stream); if (stateCol.nullable()) { auto nullMask = cudf::copy_bitmask(stateCol, stream, mr); @@ -154,6 +151,10 @@ std::unique_ptr serializeDecimalSumState( "Too many rows to serialize decimal sum state (row count is {})", numRows); + if (numRows == 0) { + return cudf::make_empty_column(cudf::type_id::STRING); + } + auto const rowCount = static_cast(numRows); auto const charsBytes = @@ -190,30 +191,28 @@ std::unique_ptr serializeDecimalSumState( rowCount, stream); - if (numRows > 0) { - auto charsPtr = reinterpret_cast(charsBuf.data()); - const void* offsetsPtr = useLargeOffsets - ? static_cast(offsetsView.data()) - : static_cast(offsetsView.data()); - const auto sumType = sumCol.type().id(); - VELOX_CHECK( - sumType == cudf::type_id::DECIMAL64 || - sumType == cudf::type_id::DECIMAL128, - "Unsupported decimal sum column type (type is {})", - static_cast(sumType)); - const void* sumPtr = sumType == cudf::type_id::DECIMAL64 - ? static_cast(sumCol.data()) - : static_cast(sumCol.data<__int128_t>()); - detail::packDecimalSumState( - sumType, - useLargeOffsets, - sumPtr, - countCol.data(), - offsetsPtr, - charsPtr, - rowCount, - stream); - } + auto charsPtr = reinterpret_cast(charsBuf.data()); + const void* offsetsPtr = useLargeOffsets + ? static_cast(offsetsView.data()) + : static_cast(offsetsView.data()); + const auto sumType = sumCol.type().id(); + VELOX_CHECK( + sumType == cudf::type_id::DECIMAL64 || + sumType == cudf::type_id::DECIMAL128, + "Unsupported decimal sum column type (type is {})", + static_cast(sumType)); + const void* sumPtr = sumType == cudf::type_id::DECIMAL64 + ? static_cast(sumCol.data()) + : static_cast(sumCol.data<__int128_t>()); + detail::packDecimalSumState( + sumType, + useLargeOffsets, + sumPtr, + countCol.data(), + offsetsPtr, + charsPtr, + rowCount, + stream); auto [nullMask, nullCount] = detail::buildStateValidityMask(sumCol, countCol, stream, get_output_mr()); From f76cb386b7e824ec5727a45e6bcb6605ea376323 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Mon, 1 Jun 2026 22:29:51 -0700 Subject: [PATCH 38/64] Groupby and reduce refactors and simplification --- velox/experimental/cudf/exec/CudfGroupby.cpp | 36 ++------- velox/experimental/cudf/exec/CudfReduce.cpp | 77 +++++++++----------- 2 files changed, 41 insertions(+), 72 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 932cdd66c5f..92946df06ef 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -129,7 +129,11 @@ void addDecimalSumCountRequestsAfterDecode( cudf::make_sum_aggregation()); } -void addDecimalIntermediateSumCountRequests( +// Decodes serialized state and adds sum + count groupby requests, used by the +// intermediate step (both SUM and AVG) and the final AVG step. resultType is +// DECIMAL for final AVG and DECIMAL or VARBINARY for intermediate; VARBINARY +// carries no scale, so decode at scale 0. +void addDecimalDecodedSumCountRequests( cudf::table_view const& tbl, uint32_t inputIndex, const TypePtr& resultType, @@ -140,7 +144,6 @@ void addDecimalIntermediateSumCountRequests( std::unique_ptr& decodedSum, std::unique_ptr& decodedCount) { validateIntermediateColumnType(tbl.column(inputIndex)); - // resultType here could be DECIMAL or VARBINARY auto scale = resultType->isDecimal() ? getDecimalPrecisionScale(*resultType).second : 0; @@ -155,29 +158,6 @@ void addDecimalIntermediateSumCountRequests( decodedCount); } -void addDecimalFinalAvgSumCountRequests( - cudf::table_view const& tbl, - uint32_t inputIndex, - const TypePtr& resultType, - std::vector& requests, - rmm::cuda_stream_view stream, - uint32_t& sumIdx, - uint32_t& countIdx, - std::unique_ptr& decodedSum, - std::unique_ptr& decodedCount) { - validateIntermediateColumnType(tbl.column(inputIndex)); - auto scale = getDecimalPrecisionScale(*resultType).second; - addDecimalSumCountRequestsAfterDecode( - tbl.column(inputIndex), - scale, - requests, - stream, - sumIdx, - countIdx, - decodedSum, - decodedCount); -} - void addDecimalFinalSumOnlyRequest( cudf::table_view const& tbl, uint32_t inputIndex, @@ -233,7 +213,7 @@ struct GroupbyDecimalSumAggregator : GroupbyAggregator { std::vector& requests, rmm::cuda_stream_view stream) override { if (step == core::AggregationNode::Step::kIntermediate) { - addDecimalIntermediateSumCountRequests( + addDecimalDecodedSumCountRequests( tbl, inputIndex, resultType, @@ -302,7 +282,7 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { std::vector& requests, rmm::cuda_stream_view stream) override { if (step == core::AggregationNode::Step::kIntermediate) { - addDecimalIntermediateSumCountRequests( + addDecimalDecodedSumCountRequests( tbl, inputIndex, resultType, @@ -313,7 +293,7 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { decodedSum_, decodedCount_); } else if (step == core::AggregationNode::Step::kFinal) { - addDecimalFinalAvgSumCountRequests( + addDecimalDecodedSumCountRequests( tbl, inputIndex, resultType, diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index c40def3adea..6c1c12f5c8c 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -253,6 +253,18 @@ struct ReduceMeanAggregator : ReduceAggregator { } }; +// Materializes reduced sum/count scalars into 1-row columns. +cudf_velox::DecimalSumStateColumns makeSumCountColumns( + cudf::scalar const& sumScalar, + cudf::scalar const& countScalar, + rmm::cuda_stream_view stream) { + cudf_velox::DecimalSumStateColumns cols; + cols.sum = cudf::make_column_from_scalar(sumScalar, 1, stream, get_temp_mr()); + cols.count = + cudf::make_column_from_scalar(countScalar, 1, stream, get_temp_mr()); + return cols; +} + std::unique_ptr partialDecimalSumCountToSerializedString( cudf::column_view inputCol, rmm::cuda_stream_view stream) { @@ -269,17 +281,17 @@ std::unique_ptr partialDecimalSumCountToSerializedString( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - // sumCol/countCol are consumed by the serialize/finalize call below and never - // leave the operator, so they come from the temporary memory resource. - auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); - auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); + auto cols = makeSumCountColumns(*sumScalar, *countScalar, stream); return serializeDecimalPartialOrIntermediateState( - std::move(sumCol), std::move(countCol), stream); + std::move(cols.sum), std::move(cols.count), stream); } -std::unique_ptr intermediateDecimalMergeSerializedString( +// Decodes serialized decimal SUM state, sums the per-row partial sums and +// counts, and returns them as 1-row columns. Shared by the intermediate and +// final reduce steps before re-serializing or finalizing. The merged columns +// are consumed by the caller and never leave the operator, so they come from +// the temporary memory resource. +cudf_velox::DecimalSumStateColumns mergeSerializedDecimalSumState( cudf::column_view inputCol, int32_t scale, rmm::cuda_stream_view stream) { @@ -298,14 +310,16 @@ std::unique_ptr intermediateDecimalMergeSerializedString( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - // sumCol/countCol are consumed by the serialize/finalize call below and never - // leave the operator, so they come from the temporary memory resource. - auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); - auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); + return makeSumCountColumns(*sumScalar, *countScalar, stream); +} + +std::unique_ptr intermediateDecimalMergeSerializedString( + cudf::column_view inputCol, + int32_t scale, + rmm::cuda_stream_view stream) { + auto merged = mergeSerializedDecimalSumState(inputCol, scale, stream); return serializeDecimalPartialOrIntermediateState( - std::move(sumCol), std::move(countCol), stream); + std::move(merged.sum), std::move(merged.count), stream); } std::unique_ptr finalDecimalAvgFromSerializedString( @@ -313,29 +327,9 @@ std::unique_ptr finalDecimalAvgFromSerializedString( int32_t scale, TypePtr const& resultType, rmm::cuda_stream_view stream) { - auto const sumAgg = cudf::make_sum_aggregation(); - auto sumAndCount = - cudf_velox::deserializeDecimalSumState(inputCol, scale, stream); - auto sumScalar = cudf::reduce( - sumAndCount.sum->view(), - *sumAgg, - sumAndCount.sum->view().type(), - stream, - get_temp_mr()); - auto countScalar = cudf::reduce( - sumAndCount.count->view(), - *sumAgg, - cudf::data_type{cudf::type_id::INT64}, - stream, - get_temp_mr()); - // sumCol/countCol are consumed by the serialize/finalize call below and never - // leave the operator, so they come from the temporary memory resource. - auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); - auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); + auto merged = mergeSerializedDecimalSumState(inputCol, scale, stream); return finalizeDecimalAverage( - std::move(sumCol), std::move(countCol), resultType, stream); + std::move(merged.sum), std::move(merged.count), resultType, stream); } std::unique_ptr singleDecimalAvgFromRawColumn( @@ -355,14 +349,9 @@ std::unique_ptr singleDecimalAvgFromRawColumn( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - // sumCol/countCol are consumed by the serialize/finalize call below and never - // leave the operator, so they come from the temporary memory resource. - auto sumCol = - cudf::make_column_from_scalar(*sumScalar, 1, stream, get_temp_mr()); - auto countCol = - cudf::make_column_from_scalar(*countScalar, 1, stream, get_temp_mr()); + auto cols = makeSumCountColumns(*sumScalar, *countScalar, stream); return finalizeDecimalAverage( - std::move(sumCol), std::move(countCol), resultType, stream); + std::move(cols.sum), std::move(cols.count), resultType, stream); } std::unique_ptr singleOrRawDecimalSumWithCast( From 4e186bc416fbef5b5800b61ea0b75c21c925f61b Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Tue, 2 Jun 2026 19:08:22 -0700 Subject: [PATCH 39/64] Address various PR comments on kernels --- .../cudf/exec/DecimalAggregationKernelsGpu.cu | 167 +++++++++++------- 1 file changed, 103 insertions(+), 64 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu index f5aeddaa24f..9f1fe6d7685 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu @@ -17,13 +17,18 @@ #include "velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h" #include -#include +#include +#include #include +#include + #include +#include #include #include #include +#include #include @@ -57,7 +62,7 @@ splitToWords(__int128_t value, int64_t& upper, uint64_t& lower) { template struct FillOffsetsFunctor { - OffsetT* offsets; + cuda::std::span offsets; __device__ void operator()(int32_t idx) const { int64_t offset = static_cast(idx) * detail::kDecimalSumStateSize; @@ -67,9 +72,9 @@ struct FillOffsetsFunctor { template struct PackStateFunctor { - const SumT* sums; - const int64_t* counts; - const OffsetT* offsets; + cuda::std::span sums; + cuda::std::span counts; + cuda::std::span offsets; uint8_t* chars; __device__ void operator()(int32_t idx) const { @@ -87,10 +92,10 @@ struct PackStateFunctor { template struct UnpackStateFunctor { - const OffsetT* offsets; + cuda::std::span offsets; const uint8_t* chars; - __int128_t* sums; - int64_t* counts; + cuda::std::span<__int128_t> sums; + cuda::std::span counts; __device__ void operator()(int32_t idx) const { int64_t offset = static_cast(offsets[idx]); @@ -102,9 +107,9 @@ struct UnpackStateFunctor { template struct AvgRoundFunctor { - const SumT* sums; - const int64_t* counts; - SumT* out; + cuda::std::span sums; + cuda::std::span counts; + cuda::std::span out; __device__ void operator()(int32_t idx) const { auto count = counts[idx]; @@ -124,53 +129,61 @@ struct AvgRoundFunctor { template void launchFillOffsets( - OffsetT* offsets, - int32_t numRows, + cuda::std::span offsets, rmm::cuda_stream_view stream) { FillOffsetsFunctor op{offsets}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows + 1, op, stream.value()); + thrust::counting_iterator(0), + static_cast(offsets.size()), + op, + stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } template void launchPackState( - const SumT* sums, - const int64_t* counts, - const OffsetT* offsets, + cuda::std::span sums, + cuda::std::span counts, + cuda::std::span offsets, uint8_t* chars, - int32_t numRows, rmm::cuda_stream_view stream) { PackStateFunctor op{sums, counts, offsets, chars}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows, op, stream.value()); + thrust::counting_iterator(0), + static_cast(sums.size()), + op, + stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } template void launchUnpackState( - const OffsetT* offsets, + cuda::std::span offsets, const uint8_t* chars, - __int128_t* sums, - int64_t* counts, - int32_t numRows, + cuda::std::span<__int128_t> sums, + cuda::std::span counts, rmm::cuda_stream_view stream) { UnpackStateFunctor op{offsets, chars, sums, counts}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows, op, stream.value()); + thrust::counting_iterator(0), + static_cast(sums.size()), + op, + stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } template void launchAvgRound( - const SumT* sums, - const int64_t* counts, - SumT* out, - int32_t numRows, + cuda::std::span sums, + cuda::std::span counts, + cuda::std::span out, rmm::cuda_stream_view stream) { AvgRoundFunctor op{sums, counts, out}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), numRows, op, stream.value()); + thrust::counting_iterator(0), + static_cast(out.size()), + op, + stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } @@ -198,9 +211,21 @@ std::pair buildStateValidityMaskImpl( auto sumDeviceView = cudf::column_device_view::create(sumCol, stream); auto countDeviceView = cudf::column_device_view::create(countCol, stream); StateValidPredicate pred{*sumDeviceView, *countDeviceView}; - auto begin = thrust::make_counting_iterator(0); - auto end = begin + numRows; - return cudf::detail::valid_if(begin, end, pred, stream, mr); + // Build a BOOL8 column of per-row validity, then convert via the public API. + auto bools = cudf::make_fixed_width_column( + cudf::data_type{cudf::type_id::BOOL8}, + numRows, + cudf::mask_state::UNALLOCATED, + stream, + mr); + thrust::transform( + rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(numRows), + bools->mutable_view().begin(), + pred); + auto [mask, nullCount] = cudf::bools_to_mask(bools->view(), stream, mr); + return {std::move(*mask), nullCount}; } } // namespace @@ -212,10 +237,16 @@ void fillOffsetsForDecimalSumState( void* offsetsMutable, int32_t numRows, rmm::cuda_stream_view stream) { + // The offsets buffer holds numRows + 1 entries. + auto const n = static_cast(numRows) + 1; if (use64BitOffsets) { - launchFillOffsets(static_cast(offsetsMutable), numRows, stream); + launchFillOffsets( + cuda::std::span{static_cast(offsetsMutable), n}, + stream); } else { - launchFillOffsets(static_cast(offsetsMutable), numRows, stream); + launchFillOffsets( + cuda::std::span{static_cast(offsetsMutable), n}, + stream); } } @@ -228,42 +259,46 @@ void packDecimalSumState( uint8_t* chars, int32_t numRows, rmm::cuda_stream_view stream) { + auto const n = static_cast(numRows); + cuda::std::span counts{countPtr, n}; if (use64BitOffsets) { - auto offsets = static_cast(offsetsPtr); + cuda::std::span offsets{ + static_cast(offsetsPtr), n}; if (sumType == cudf::type_id::DECIMAL64) { launchPackState( - static_cast(sumPtr), - countPtr, + cuda::std::span{ + static_cast(sumPtr), n}, + counts, offsets, chars, - numRows, stream); } else { launchPackState( - static_cast(sumPtr), - countPtr, + cuda::std::span{ + static_cast(sumPtr), n}, + counts, offsets, chars, - numRows, stream); } } else { - auto offsets = static_cast(offsetsPtr); + cuda::std::span offsets{ + static_cast(offsetsPtr), n}; if (sumType == cudf::type_id::DECIMAL64) { launchPackState( - static_cast(sumPtr), - countPtr, + cuda::std::span{ + static_cast(sumPtr), n}, + counts, offsets, chars, - numRows, stream); } else { launchPackState( - static_cast(sumPtr), - countPtr, + cuda::std::span{ + static_cast(sumPtr), n}, + counts, offsets, chars, - numRows, stream); } } @@ -277,21 +312,24 @@ void unpackDecimalSumState( int64_t* counts, int32_t numRows, rmm::cuda_stream_view stream) { + auto const n = static_cast(numRows); + cuda::std::span<__int128_t> sumsSpan{sums, n}; + cuda::std::span countsSpan{counts, n}; if (offsets64) { launchUnpackState( - static_cast(offsetsPtr), + cuda::std::span{ + static_cast(offsetsPtr), n}, chars, - sums, - counts, - numRows, + sumsSpan, + countsSpan, stream); } else { launchUnpackState( - static_cast(offsetsPtr), + cuda::std::span{ + static_cast(offsetsPtr), n}, chars, - sums, - counts, - numRows, + sumsSpan, + countsSpan, stream); } } @@ -303,19 +341,20 @@ void averageRoundDecimalSum( void* out, int32_t numRows, rmm::cuda_stream_view stream) { + auto const n = static_cast(numRows); + cuda::std::span countsSpan{counts, n}; if (sumType == cudf::type_id::DECIMAL64) { launchAvgRound( - static_cast(sums), - counts, - static_cast(out), - numRows, + cuda::std::span{static_cast(sums), n}, + countsSpan, + cuda::std::span{static_cast(out), n}, stream); } else { launchAvgRound( - static_cast(sums), - counts, - static_cast<__int128_t*>(out), - numRows, + cuda::std::span{ + static_cast(sums), n}, + countsSpan, + cuda::std::span<__int128_t>{static_cast<__int128_t*>(out), n}, stream); } } From 7618f6fd9e5e826606949dd8652b3a616d5f4220 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Wed, 3 Jun 2026 16:45:12 -0700 Subject: [PATCH 40/64] Address renaming comments on PR --- velox/experimental/cudf/exec/CMakeLists.txt | 6 +++--- velox/experimental/cudf/exec/CudfAggregation.cpp | 4 ++-- velox/experimental/cudf/exec/CudfAggregation.h | 5 ++++- velox/experimental/cudf/exec/CudfGroupby.cpp | 8 ++++---- velox/experimental/cudf/exec/CudfReduce.cpp | 8 ++++---- ...gationKernelsGpu.cu => DecimalAggregationDevice.cu} | 2 +- ...regationKernelsGpu.h => DecimalAggregationDevice.h} | 0 ...egationCommon.cpp => DecimalAggregationHostOps.cpp} | 4 ++-- ...AggregationCommon.h => DecimalAggregationHostOps.h} | 0 ...regationKernels.cpp => DecimalAggregationState.cpp} | 4 ++-- ...lAggregationKernels.h => DecimalAggregationState.h} | 0 .../cudf/expression/DecimalExpressionKernels.cpp | 6 +++--- .../cudf/expression/DecimalExpressionKernelsGpu.cu | 6 +++--- .../cudf/expression/DecimalExpressionKernelsGpu.h | 10 +++++----- .../experimental/cudf/tests/DecimalAggregationTest.cpp | 2 +- 15 files changed, 34 insertions(+), 31 deletions(-) rename velox/experimental/cudf/exec/{DecimalAggregationKernelsGpu.cu => DecimalAggregationDevice.cu} (99%) rename velox/experimental/cudf/exec/{DecimalAggregationKernelsGpu.h => DecimalAggregationDevice.h} (100%) rename velox/experimental/cudf/exec/{DecimalAggregationCommon.cpp => DecimalAggregationHostOps.cpp} (95%) rename velox/experimental/cudf/exec/{DecimalAggregationCommon.h => DecimalAggregationHostOps.h} (100%) rename velox/experimental/cudf/exec/{DecimalAggregationKernels.cpp => DecimalAggregationState.cpp} (98%) rename velox/experimental/cudf/exec/{DecimalAggregationKernels.h => DecimalAggregationState.h} (100%) diff --git a/velox/experimental/cudf/exec/CMakeLists.txt b/velox/experimental/cudf/exec/CMakeLists.txt index 018e6a39640..fd1ad02ab8a 100644 --- a/velox/experimental/cudf/exec/CMakeLists.txt +++ b/velox/experimental/cudf/exec/CMakeLists.txt @@ -33,9 +33,9 @@ add_library( CudfReduce.cpp CudfTopN.cpp DebugUtil.cpp - DecimalAggregationCommon.cpp - DecimalAggregationKernels.cpp - DecimalAggregationKernelsGpu.cu + DecimalAggregationHostOps.cpp + DecimalAggregationState.cpp + DecimalAggregationDevice.cu GpuResources.cpp OperatorAdapters.cpp PrestoAggregateFunctions.cpp diff --git a/velox/experimental/cudf/exec/CudfAggregation.cpp b/velox/experimental/cudf/exec/CudfAggregation.cpp index 79428e30f3a..3feb3fe4df1 100644 --- a/velox/experimental/cudf/exec/CudfAggregation.cpp +++ b/velox/experimental/cudf/exec/CudfAggregation.cpp @@ -136,7 +136,7 @@ std::vector resolveAggregateInfos( const auto resultType = exec::isPartialOutput(companionStep) ? exec::resolveIntermediateType(originalName, aggregate.rawInputTypes) : outputType->childAt(numKeys + i); - const auto isDecimalInput = aggregate.rawInputTypes.size() == 1 && + const auto isDecimalAggregate = aggregate.rawInputTypes.size() == 1 && aggregate.rawInputTypes[0]->isDecimal(); params.emplace_back( @@ -148,7 +148,7 @@ std::vector resolveAggregateInfos( isCountFunctionName(aggregate.call->name()) ? std::make_optional(getCountInputKind(aggregate, constants[i])) : std::nullopt, - isDecimalInput); + isDecimalAggregate); } return params; } diff --git a/velox/experimental/cudf/exec/CudfAggregation.h b/velox/experimental/cudf/exec/CudfAggregation.h index d0ea9676d0c..f00b52a4d60 100644 --- a/velox/experimental/cudf/exec/CudfAggregation.h +++ b/velox/experimental/cudf/exec/CudfAggregation.h @@ -62,7 +62,10 @@ struct ResolvedAggregateInfo { VectorPtr constant; TypePtr resultType; std::optional countInputKind; - bool isDecimalInput; + // True if the aggregate was declared on a decimal raw input in the plan. + // Routing keys off the function family, not the physical batch type (which is + // VARBINARY/STRING on intermediate and final steps). + bool isDecimalAggregate; }; // Parse aggregate inputs from the aggregation node and resolve companion steps, diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 92946df06ef..7cfcc8e80fd 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -18,8 +18,8 @@ #include "velox/experimental/cudf/CudfNoDefaults.h" #include "velox/experimental/cudf/exec/CudfFilterProject.h" #include "velox/experimental/cudf/exec/CudfGroupby.h" -#include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" -#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" +#include "velox/experimental/cudf/exec/DecimalAggregationHostOps.h" +#include "velox/experimental/cudf/exec/DecimalAggregationState.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/Utilities.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" @@ -753,7 +753,7 @@ std::unique_ptr createGroupbyAggregator( auto const& kind = p.kind; auto prefix = cudf_velox::CudfConfig::getInstance().functionNamePrefix; if (kind.rfind(prefix + "sum", 0) == 0) { - if (p.isDecimalInput) { + if (p.isDecimalAggregate) { return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } @@ -770,7 +770,7 @@ std::unique_ptr createGroupbyAggregator( return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "avg", 0) == 0) { - if (p.isDecimalInput) { + if (p.isDecimalAggregate) { return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 6c1c12f5c8c..9ba05c9c801 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -19,8 +19,8 @@ #include "velox/experimental/cudf/exec/CudfAggregation.h" #include "velox/experimental/cudf/exec/CudfFilterProject.h" #include "velox/experimental/cudf/exec/CudfReduce.h" -#include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" -#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" +#include "velox/experimental/cudf/exec/DecimalAggregationHostOps.h" +#include "velox/experimental/cudf/exec/DecimalAggregationState.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/Utilities.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" @@ -668,7 +668,7 @@ std::unique_ptr createReduceAggregator( auto const& kind = p.kind; auto prefix = cudf_velox::CudfConfig::getInstance().functionNamePrefix; if (kind.rfind(prefix + "sum", 0) == 0) { - if (p.isDecimalInput) { + if (p.isDecimalAggregate) { return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } @@ -685,7 +685,7 @@ std::unique_ptr createReduceAggregator( return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } else if (kind.rfind(prefix + "avg", 0) == 0) { - if (p.isDecimalInput) { + if (p.isDecimalAggregate) { return std::make_unique( p.companionStep, p.inputIndex, p.constant, p.resultType); } diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu similarity index 99% rename from velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu rename to velox/experimental/cudf/exec/DecimalAggregationDevice.cu index 9f1fe6d7685..f386ff142ae 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h" +#include "velox/experimental/cudf/exec/DecimalAggregationDevice.h" #include #include diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h b/velox/experimental/cudf/exec/DecimalAggregationDevice.h similarity index 100% rename from velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h rename to velox/experimental/cudf/exec/DecimalAggregationDevice.h diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp similarity index 95% rename from velox/experimental/cudf/exec/DecimalAggregationCommon.cpp rename to velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp index b4696588d40..331c98c42ca 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationCommon.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp @@ -15,8 +15,8 @@ */ #include "velox/experimental/cudf/CudfNoDefaults.h" -#include "velox/experimental/cudf/exec/DecimalAggregationCommon.h" -#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" +#include "velox/experimental/cudf/exec/DecimalAggregationHostOps.h" +#include "velox/experimental/cudf/exec/DecimalAggregationState.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" diff --git a/velox/experimental/cudf/exec/DecimalAggregationCommon.h b/velox/experimental/cudf/exec/DecimalAggregationHostOps.h similarity index 100% rename from velox/experimental/cudf/exec/DecimalAggregationCommon.h rename to velox/experimental/cudf/exec/DecimalAggregationHostOps.h diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp similarity index 98% rename from velox/experimental/cudf/exec/DecimalAggregationKernels.cpp rename to velox/experimental/cudf/exec/DecimalAggregationState.cpp index 3a0e69897a1..15dd1a51147 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationKernels.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -15,8 +15,8 @@ */ #include "velox/experimental/cudf/CudfNoDefaults.h" -#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" -#include "velox/experimental/cudf/exec/DecimalAggregationKernelsGpu.h" +#include "velox/experimental/cudf/exec/DecimalAggregationState.h" +#include "velox/experimental/cudf/exec/DecimalAggregationDevice.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/common/base/Exceptions.h" diff --git a/velox/experimental/cudf/exec/DecimalAggregationKernels.h b/velox/experimental/cudf/exec/DecimalAggregationState.h similarity index 100% rename from velox/experimental/cudf/exec/DecimalAggregationKernels.h rename to velox/experimental/cudf/exec/DecimalAggregationState.h diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp b/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp index 5a9e1a619ee..a419bf5981d 100644 --- a/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp +++ b/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp @@ -143,7 +143,7 @@ std::unique_ptr decimalDivide( auto out = cudf::make_fixed_width_column( outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); - detail::launchDecimalDivideColumnColumn( + detail::decimalDivideColumnColumn( inType, outType, lhs, rhs, out->mutable_view(), aRescale, stream); // Scatter nulls where divisor is zero. @@ -187,7 +187,7 @@ std::unique_ptr decimalDivide( "Unexpected output type for decimal divide"); } - detail::launchDecimalDivideColumnRhsScalar( + detail::decimalDivideColumnScalar( inType, outType, lhs, rhsValue, out->mutable_view(), aRescale, stream); return out; @@ -233,7 +233,7 @@ std::unique_ptr decimalDivide( "Unexpected output type for decimal divide"); } - detail::launchDecimalDivideLhsScalarColumn( + detail::decimalDivideScalarColumn( inType, outType, lhsValue, rhs, out->mutable_view(), aRescale, stream); // Scatter nulls where divisor is zero. diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu index d6a6003d0d3..29b7c13f59d 100644 --- a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu +++ b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu @@ -156,7 +156,7 @@ void launchDivideKernelRhsScalar( namespace detail { -void launchDecimalDivideColumnColumn( +void decimalDivideColumnColumn( cudf::type_id inType, cudf::type_id outType, const cudf::column_view& lhs, @@ -175,7 +175,7 @@ void launchDecimalDivideColumnColumn( } } -void launchDecimalDivideColumnRhsScalar( +void decimalDivideColumnScalar( cudf::type_id inType, cudf::type_id outType, const cudf::column_view& lhs, @@ -197,7 +197,7 @@ void launchDecimalDivideColumnRhsScalar( } } -void launchDecimalDivideLhsScalarColumn( +void decimalDivideScalarColumn( cudf::type_id inType, cudf::type_id outType, __int128_t lhsValue, diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h index 2d20360c41b..310be3ee834 100644 --- a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h +++ b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h @@ -28,7 +28,7 @@ namespace facebook::velox::cudf_velox::detail { // rhs with half-away-from-zero rounding on the remainder, writing into out. // Zero divisors produce a numeric zero in out (callers patch nulls). inType / // outType select DECIMAL64 vs DECIMAL128 storage widths for inputs and result. -void launchDecimalDivideColumnColumn( +void decimalDivideColumnColumn( cudf::type_id inType, cudf::type_id outType, const cudf::column_view& lhs, @@ -37,9 +37,9 @@ void launchDecimalDivideColumnColumn( int32_t aRescale, rmm::cuda_stream_view stream); -// Same kernel math as launchDecimalDivideColumnColumn, but rhs is a single +// Same kernel math as decimalDivideColumnColumn, but rhs is a single // __int128_t decimal payload (already decoded from a cuDF scalar). -void launchDecimalDivideColumnRhsScalar( +void decimalDivideColumnScalar( cudf::type_id inType, cudf::type_id outType, const cudf::column_view& lhs, @@ -48,9 +48,9 @@ void launchDecimalDivideColumnRhsScalar( int32_t aRescale, rmm::cuda_stream_view stream); -// Same kernel math as launchDecimalDivideColumnColumn, but lhs is a single +// Same kernel math as decimalDivideColumnColumn, but lhs is a single // __int128_t decimal payload and rhs is per-row. -void launchDecimalDivideLhsScalarColumn( +void decimalDivideScalarColumn( cudf::type_id inType, cudf::type_id outType, __int128_t lhsValue, diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index d768f4c8cfb..ddb20dd832a 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -15,7 +15,7 @@ */ #include "velox/experimental/cudf/CudfConfig.h" -#include "velox/experimental/cudf/exec/DecimalAggregationKernels.h" +#include "velox/experimental/cudf/exec/DecimalAggregationState.h" #include "velox/experimental/cudf/exec/ToCudf.h" #include "velox/experimental/cudf/exec/VeloxCudfInterop.h" From 160f46ac071659a65805b408c3cd3ffb3f0f51f8 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Wed, 3 Jun 2026 16:47:08 -0700 Subject: [PATCH 41/64] Pre-commit --- velox/experimental/cudf/exec/DecimalAggregationState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index 15dd1a51147..14147b486db 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -15,8 +15,8 @@ */ #include "velox/experimental/cudf/CudfNoDefaults.h" -#include "velox/experimental/cudf/exec/DecimalAggregationState.h" #include "velox/experimental/cudf/exec/DecimalAggregationDevice.h" +#include "velox/experimental/cudf/exec/DecimalAggregationState.h" #include "velox/experimental/cudf/exec/GpuResources.h" #include "velox/common/base/Exceptions.h" From 82d29d23f2e77b98ac78888a3806aeff0e8ed399 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Thu, 4 Jun 2026 11:49:41 -0700 Subject: [PATCH 42/64] Address additional comments --- velox/experimental/cudf/exec/CudfReduce.cpp | 4 ++ .../cudf/exec/DecimalAggregationDevice.cu | 1 + .../cudf/exec/DecimalAggregationDevice.h | 52 ++++++++++++++----- .../cudf/exec/DecimalAggregationHostOps.h | 4 +- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 9ba05c9c801..7f3e733973b 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -405,6 +405,10 @@ std::unique_ptr reduceFinalDecimalAvgFromSerializedColumn( inputCol, scale, outputType, stream); } +// Decimal SUM and AVG use dedicated aggregators rather than cudf::reduce's +// built-in sum/mean: partial/intermediate state is VARBINARY-encoded sum+count +// (see DecimalAggregationState), and the final divide needs decimal half-up +// rounding. struct ReduceDecimalSumAggregator : ReduceAggregator { ReduceDecimalSumAggregator( core::AggregationNode::Step step, diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index f386ff142ae..fad1e19f6f4 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -105,6 +105,7 @@ struct UnpackStateFunctor { } }; +// Half-up sum/count divide for AVG. template struct AvgRoundFunctor { cuda::std::span sums; diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.h b/velox/experimental/cudf/exec/DecimalAggregationDevice.h index 0c44c11010e..50cd53c0ba2 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.h +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.h @@ -32,17 +32,27 @@ namespace facebook::velox::cudf_velox::detail { // words). constexpr int32_t kDecimalSumStateSize = 32; -// Writes strings-style prefix offsets into offsetsMutable for numRows + 1 -// entries: offset[i] == i * kDecimalSumStateSize (INT32 or INT64 elements). +// Writes strings-style prefix offsets: offset[i] == i * kDecimalSumStateSize. +// @param use64BitOffsets whether offsets are INT64 (else INT32). +// @param offsetsMutable output buffer of numRows + 1 offset elements. +// @param numRows number of payload rows. +// @param stream CUDA stream for the launch. void fillOffsetsForDecimalSumState( bool use64BitOffsets, void* offsetsMutable, int32_t numRows, rmm::cuda_stream_view stream); -// For each row, writes kDecimalSumStateSize bytes into chars at the byte offset -// given by offsetsPtr, encoding the partial sum (DECIMAL64 or DECIMAL128) and -// int64 count into the device struct layout used for VARBINARY interchange. +// Encodes each row's partial sum and count into the fixed-width device layout +// used for VARBINARY interchange. +// @param sumType element type of sumPtr (DECIMAL64 or DECIMAL128). +// @param use64BitOffsets whether offsetsPtr is INT64 (else INT32). +// @param sumPtr per-row sums. +// @param countPtr per-row int64 counts. +// @param offsetsPtr per-row byte offsets into chars. +// @param chars output payload buffer. +// @param numRows number of rows. +// @param stream CUDA stream for the launch. void packDecimalSumState( cudf::type_id sumType, bool use64BitOffsets, @@ -53,8 +63,14 @@ void packDecimalSumState( int32_t numRows, rmm::cuda_stream_view stream); -// Inverse of packDecimalSumState: reads fixed-width payloads via offsetsPtr -// (INT32 or INT64 string offsets) and fills per-row DECIMAL128 sums and counts. +// Inverse of packDecimalSumState. +// @param offsets64 whether offsetsPtr is INT64 (else INT32). +// @param offsetsPtr per-row byte offsets into chars. +// @param chars packed payload buffer. +// @param sums output per-row DECIMAL128 sums. +// @param counts output per-row counts. +// @param numRows number of rows. +// @param stream CUDA stream for the launch. void unpackDecimalSumState( bool offsets64, const void* offsetsPtr, @@ -64,10 +80,14 @@ void unpackDecimalSumState( int32_t numRows, rmm::cuda_stream_view stream); -// Per-row average from intermediate sum/count: integer divide of abs(sum) by -// count with half-up bias (add count/2 before dividing), then restore sign; -// count == 0 writes a numeric zero (validity is applied separately). Output -// element type matches sumType (DECIMAL64 or DECIMAL128). +// Per-row half-up integer divide of sum by count; count == 0 writes zero +// (validity is applied separately). +// @param sumType element type of sums/out (DECIMAL64 or DECIMAL128). +// @param sums per-row sums. +// @param counts per-row counts. +// @param out output per-row averages. +// @param numRows number of rows. +// @param stream CUDA stream for the launch. void averageRoundDecimalSum( cudf::type_id sumType, const void* sums, @@ -76,9 +96,13 @@ void averageRoundDecimalSum( int32_t numRows, rmm::cuda_stream_view stream); -// Builds a bitmask for rows where both sum and count are valid and count is -// non-zero (via cudf::detail::valid_if), for use when serializing state or -// finalizing averages. +// Builds a null mask for rows where sum and count are both valid and count is +// non-zero, for serializing state or finalizing averages. +// @param sumCol decoded sum column. +// @param countCol decoded count column. +// @param stream CUDA stream for the launch. +// @param mr memory resource for the returned mask. +// @return {null mask buffer, null count}. std::pair buildStateValidityMask( const cudf::column_view& sumCol, const cudf::column_view& countCol, diff --git a/velox/experimental/cudf/exec/DecimalAggregationHostOps.h b/velox/experimental/cudf/exec/DecimalAggregationHostOps.h index 851bd9874d6..650b9ab6a32 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationHostOps.h +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.h @@ -28,7 +28,9 @@ namespace facebook::velox::cudf_velox { // Asserts that a column holds serialized decimal aggregate state in the form // Velox uses for VARBINARY: a cuDF STRING column whose bytes are the packed -// sum/count payloads (see serializeDecimalSumState). +// sum/count payloads (see serializeDecimalSumState). The payload does not carry +// scale, so VARBINARY intermediate steps decode at scale 0; the real scale is +// applied at final cast time. void validateIntermediateColumnType(cudf::column_view const& column); // Casts a DECIMAL64 column up to DECIMAL128 (scale preserved) so a subsequent From b3ad1f874651f90c541cf378aa5d00e0ea35c7c3 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Thu, 4 Jun 2026 13:33:43 -0700 Subject: [PATCH 43/64] Address build config PR comments --- velox/experimental/cudf/CMakeLists.txt | 4 ++++ velox/experimental/cudf/exec/CMakeLists.txt | 2 -- velox/experimental/cudf/expression/CMakeLists.txt | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/velox/experimental/cudf/CMakeLists.txt b/velox/experimental/cudf/CMakeLists.txt index edbb5feeebe..fc476996efa 100644 --- a/velox/experimental/cudf/CMakeLists.txt +++ b/velox/experimental/cudf/CMakeLists.txt @@ -12,6 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Build cuDF's CUDA sources at the same C++ standard as the rest of the project. +set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) +set(CMAKE_CUDA_STANDARD_REQUIRED ${CMAKE_CXX_STANDARD_REQUIRED}) + add_subdirectory(connectors) add_subdirectory(exec) add_subdirectory(expression) diff --git a/velox/experimental/cudf/exec/CMakeLists.txt b/velox/experimental/cudf/exec/CMakeLists.txt index fd1ad02ab8a..bdec064d97f 100644 --- a/velox/experimental/cudf/exec/CMakeLists.txt +++ b/velox/experimental/cudf/exec/CMakeLists.txt @@ -45,8 +45,6 @@ add_library( VeloxCudfInterop.cpp ) -set_target_properties(velox_cudf_exec PROPERTIES CUDA_STANDARD 20 CUDA_STANDARD_REQUIRED ON) - target_link_libraries( velox_cudf_exec PUBLIC cudf::cudf diff --git a/velox/experimental/cudf/expression/CMakeLists.txt b/velox/experimental/cudf/expression/CMakeLists.txt index cb41a19be43..a29514af4d2 100644 --- a/velox/experimental/cudf/expression/CMakeLists.txt +++ b/velox/experimental/cudf/expression/CMakeLists.txt @@ -32,5 +32,3 @@ target_link_libraries( ) target_compile_options(velox_cudf_expression PRIVATE -Wno-missing-field-initializers) - -set_target_properties(velox_cudf_expression PROPERTIES CUDA_STANDARD 20 CUDA_STANDARD_REQUIRED ON) From f1d06217cafde31064b035694113bade762377a2 Mon Sep 17 00:00:00 2001 From: Matt Gara Date: Thu, 4 Jun 2026 13:34:24 -0700 Subject: [PATCH 44/64] Pre-commit --- velox/experimental/cudf/exec/CudfReduce.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 7f3e733973b..05b638edcdc 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -408,7 +408,7 @@ std::unique_ptr reduceFinalDecimalAvgFromSerializedColumn( // Decimal SUM and AVG use dedicated aggregators rather than cudf::reduce's // built-in sum/mean: partial/intermediate state is VARBINARY-encoded sum+count // (see DecimalAggregationState), and the final divide needs decimal half-up -// rounding. +// rounding. struct ReduceDecimalSumAggregator : ReduceAggregator { ReduceDecimalSumAggregator( core::AggregationNode::Step step, From f86c1926033d1b6512747c2d326805ca1d37c4fd Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 9 Jun 2026 16:14:54 -0700 Subject: [PATCH 45/64] Remove off-style comments --- velox/experimental/cudf/exec/AggregationRegistry.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/velox/experimental/cudf/exec/AggregationRegistry.cpp b/velox/experimental/cudf/exec/AggregationRegistry.cpp index 762237fa5e7..46006354204 100644 --- a/velox/experimental/cudf/exec/AggregationRegistry.cpp +++ b/velox/experimental/cudf/exec/AggregationRegistry.cpp @@ -82,7 +82,6 @@ void registerCommonAggregationFunctions( .argumentType("double") .build()}; - // Decimal sum signatures. auto decimalSumSingle = std::vector{ FunctionSignatureBuilder() .integerVariable("a_precision") @@ -343,7 +342,6 @@ void registerCommonAggregationFunctions( .argumentType("double") .build()}; - // Decimal avg signatures. auto decimalAvgSingle = std::vector{ FunctionSignatureBuilder() .integerVariable("a_precision") From bdafae00ba80d549c44d4979e5b2fc0c6a9d668d Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 9 Jun 2026 16:15:53 -0700 Subject: [PATCH 46/64] Keep alphabetical order --- velox/experimental/cudf/exec/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/velox/experimental/cudf/exec/CMakeLists.txt b/velox/experimental/cudf/exec/CMakeLists.txt index c240eae7a97..e96daead1ed 100644 --- a/velox/experimental/cudf/exec/CMakeLists.txt +++ b/velox/experimental/cudf/exec/CMakeLists.txt @@ -33,9 +33,9 @@ add_library( CudfReduce.cpp CudfTopN.cpp DebugUtil.cpp + DecimalAggregationDevice.cu DecimalAggregationHostOps.cpp DecimalAggregationState.cpp - DecimalAggregationDevice.cu GpuResources.cpp OperatorAdapters.cpp PrestoAggregateFunctions.cpp From e1e1823567298ac2abca90a60b1f4cd8a1ee2649 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 9 Jun 2026 16:30:07 -0700 Subject: [PATCH 47/64] Use cudf::size_type and cudf::type_to_name --- .../experimental/cudf/exec/DecimalAggregationDevice.cu | 8 ++++---- .../experimental/cudf/exec/DecimalAggregationDevice.h | 10 +++++----- .../experimental/cudf/exec/DecimalAggregationState.cpp | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index fad1e19f6f4..2742654b057 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -64,7 +64,7 @@ template struct FillOffsetsFunctor { cuda::std::span offsets; - __device__ void operator()(int32_t idx) const { + __device__ void operator()(cudf::size_type idx) const { int64_t offset = static_cast(idx) * detail::kDecimalSumStateSize; offsets[idx] = static_cast(offset); } @@ -77,7 +77,7 @@ struct PackStateFunctor { cuda::std::span offsets; uint8_t* chars; - __device__ void operator()(int32_t idx) const { + __device__ void operator()(cudf::size_type idx) const { int64_t offset = static_cast(offsets[idx]); auto* state = reinterpret_cast(chars + offset); int64_t upper; @@ -97,7 +97,7 @@ struct UnpackStateFunctor { cuda::std::span<__int128_t> sums; cuda::std::span counts; - __device__ void operator()(int32_t idx) const { + __device__ void operator()(cudf::size_type idx) const { int64_t offset = static_cast(offsets[idx]); auto* state = reinterpret_cast(chars + offset); counts[idx] = state->count; @@ -112,7 +112,7 @@ struct AvgRoundFunctor { cuda::std::span counts; cuda::std::span out; - __device__ void operator()(int32_t idx) const { + __device__ void operator()(cudf::size_type idx) const { auto count = counts[idx]; if (count == 0) { out[idx] = SumT{0}; diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.h b/velox/experimental/cudf/exec/DecimalAggregationDevice.h index 50cd53c0ba2..ae1c7de10f4 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.h +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.h @@ -30,7 +30,7 @@ namespace facebook::velox::cudf_velox::detail { // Size in bytes of each row's packed decimal SUM intermediate state in the // strings payload (count, overflow placeholder, and 128-bit sum split into // words). -constexpr int32_t kDecimalSumStateSize = 32; +constexpr size_t kDecimalSumStateSize = 32; // Writes strings-style prefix offsets: offset[i] == i * kDecimalSumStateSize. // @param use64BitOffsets whether offsets are INT64 (else INT32). @@ -40,7 +40,7 @@ constexpr int32_t kDecimalSumStateSize = 32; void fillOffsetsForDecimalSumState( bool use64BitOffsets, void* offsetsMutable, - int32_t numRows, + cudf::size_type numRows, rmm::cuda_stream_view stream); // Encodes each row's partial sum and count into the fixed-width device layout @@ -60,7 +60,7 @@ void packDecimalSumState( const int64_t* countPtr, const void* offsetsPtr, uint8_t* chars, - int32_t numRows, + cudf::size_type numRows, rmm::cuda_stream_view stream); // Inverse of packDecimalSumState. @@ -77,7 +77,7 @@ void unpackDecimalSumState( const uint8_t* chars, __int128_t* sums, int64_t* counts, - int32_t numRows, + cudf::size_type numRows, rmm::cuda_stream_view stream); // Per-row half-up integer divide of sum by count; count == 0 writes zero @@ -93,7 +93,7 @@ void averageRoundDecimalSum( const void* sums, const int64_t* counts, void* out, - int32_t numRows, + cudf::size_type numRows, rmm::cuda_stream_view stream); // Builds a null mask for rows where sum and count are both valid and count is diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index 14147b486db..d430cfcf84e 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -37,7 +37,7 @@ DecimalSumStateColumns deserializeDecimalSumState( VELOX_CHECK( stateCol.type().id() == cudf::type_id::STRING, "Decimal sum state requires STRING/VARBINARY column (type is {})", - static_cast(stateCol.type().id())); + cudf::type_to_name(stateCol.type())); // The decoded sum/count columns are consumed by the next groupby/reduce and // never leave the operator and should use the temporary memory resource. auto const mr = get_temp_mr(); @@ -200,7 +200,7 @@ std::unique_ptr serializeDecimalSumState( sumType == cudf::type_id::DECIMAL64 || sumType == cudf::type_id::DECIMAL128, "Unsupported decimal sum column type (type is {})", - static_cast(sumType)); + cudf::type_to_name(sumCol.type())); const void* sumPtr = sumType == cudf::type_id::DECIMAL64 ? static_cast(sumCol.data()) : static_cast(sumCol.data<__int128_t>()); @@ -231,12 +231,12 @@ std::unique_ptr computeDecimalAverage( VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, "Decimal average requires INT64 count column (type is {})", - static_cast(countCol.type().id())); + cudf::type_to_name(countCol.type())); VELOX_CHECK( sumCol.type().id() == cudf::type_id::DECIMAL64 || sumCol.type().id() == cudf::type_id::DECIMAL128, "Decimal average requires DECIMAL64 or DECIMAL128 sum column (type is {})", - static_cast(sumCol.type().id())); + cudf::type_to_name(sumCol.type())); VELOX_CHECK_EQ( sumCol.size(), countCol.size(), From 052cfc209b5212adbe25b1c8260421a8d4bf82d1 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 9 Jun 2026 16:31:54 -0700 Subject: [PATCH 48/64] Fix Doxygen function comments style --- .../cudf/exec/DecimalAggregationDevice.h | 91 +++++++++++-------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.h b/velox/experimental/cudf/exec/DecimalAggregationDevice.h index ae1c7de10f4..25411ca1a2b 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.h +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.h @@ -32,27 +32,33 @@ namespace facebook::velox::cudf_velox::detail { // words). constexpr size_t kDecimalSumStateSize = 32; -// Writes strings-style prefix offsets: offset[i] == i * kDecimalSumStateSize. -// @param use64BitOffsets whether offsets are INT64 (else INT32). -// @param offsetsMutable output buffer of numRows + 1 offset elements. -// @param numRows number of payload rows. -// @param stream CUDA stream for the launch. +/** + * Writes strings-style prefix offsets: offset[i] == i * kDecimalSumStateSize. + * + * @param use64BitOffsets whether offsets are INT64 (else INT32). + * @param offsetsMutable output buffer of numRows + 1 offset elements. + * @param numRows number of payload rows. + * @param stream CUDA stream for the launch. + */ void fillOffsetsForDecimalSumState( bool use64BitOffsets, void* offsetsMutable, cudf::size_type numRows, rmm::cuda_stream_view stream); -// Encodes each row's partial sum and count into the fixed-width device layout -// used for VARBINARY interchange. -// @param sumType element type of sumPtr (DECIMAL64 or DECIMAL128). -// @param use64BitOffsets whether offsetsPtr is INT64 (else INT32). -// @param sumPtr per-row sums. -// @param countPtr per-row int64 counts. -// @param offsetsPtr per-row byte offsets into chars. -// @param chars output payload buffer. -// @param numRows number of rows. -// @param stream CUDA stream for the launch. +/** + * Encodes each row's partial sum and count into the fixed-width device layout + * used for VARBINARY interchange. + * + * @param sumType element type of sumPtr (DECIMAL64 or DECIMAL128). + * @param use64BitOffsets whether offsetsPtr is INT64 (else INT32). + * @param sumPtr per-row sums. + * @param countPtr per-row int64 counts. + * @param offsetsPtr per-row byte offsets into chars. + * @param chars output payload buffer. + * @param numRows number of rows. + * @param stream CUDA stream for the launch. + */ void packDecimalSumState( cudf::type_id sumType, bool use64BitOffsets, @@ -63,14 +69,17 @@ void packDecimalSumState( cudf::size_type numRows, rmm::cuda_stream_view stream); -// Inverse of packDecimalSumState. -// @param offsets64 whether offsetsPtr is INT64 (else INT32). -// @param offsetsPtr per-row byte offsets into chars. -// @param chars packed payload buffer. -// @param sums output per-row DECIMAL128 sums. -// @param counts output per-row counts. -// @param numRows number of rows. -// @param stream CUDA stream for the launch. +/** + * Inverse of packDecimalSumState. + * + * @param offsets64 whether offsetsPtr is INT64 (else INT32). + * @param offsetsPtr per-row byte offsets into chars. + * @param chars packed payload buffer. + * @param sums output per-row DECIMAL128 sums. + * @param counts output per-row counts. + * @param numRows number of rows. + * @param stream CUDA stream for the launch. + */ void unpackDecimalSumState( bool offsets64, const void* offsetsPtr, @@ -80,14 +89,17 @@ void unpackDecimalSumState( cudf::size_type numRows, rmm::cuda_stream_view stream); -// Per-row half-up integer divide of sum by count; count == 0 writes zero -// (validity is applied separately). -// @param sumType element type of sums/out (DECIMAL64 or DECIMAL128). -// @param sums per-row sums. -// @param counts per-row counts. -// @param out output per-row averages. -// @param numRows number of rows. -// @param stream CUDA stream for the launch. +/** + * Per-row half-up integer divide of sum by count; count == 0 writes zero + * (validity is applied separately). + * + * @param sumType element type of sums/out (DECIMAL64 or DECIMAL128). + * @param sums per-row sums. + * @param counts per-row counts. + * @param out output per-row averages. + * @param numRows number of rows. + * @param stream CUDA stream for the launch. + */ void averageRoundDecimalSum( cudf::type_id sumType, const void* sums, @@ -96,13 +108,16 @@ void averageRoundDecimalSum( cudf::size_type numRows, rmm::cuda_stream_view stream); -// Builds a null mask for rows where sum and count are both valid and count is -// non-zero, for serializing state or finalizing averages. -// @param sumCol decoded sum column. -// @param countCol decoded count column. -// @param stream CUDA stream for the launch. -// @param mr memory resource for the returned mask. -// @return {null mask buffer, null count}. +/** + * Builds a null mask for rows where sum and count are both valid and count is + * non-zero, for serializing state or finalizing averages. + * + * @param sumCol decoded sum column. + * @param countCol decoded count column. + * @param stream CUDA stream for the launch. + * @param mr memory resource for the returned mask. + * @return {null mask buffer, null count}. + */ std::pair buildStateValidityMask( const cudf::column_view& sumCol, const cudf::column_view& countCol, From 6aa5925b02e95aa7ad19e2a771bfd76da7c52b2e Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 9 Jun 2026 16:44:49 -0700 Subject: [PATCH 49/64] Move CUDA C++ standard setting (are we sure?) --- CMakeLists.txt | 3 +++ velox/experimental/cudf/CMakeLists.txt | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9d3b34b27b..b1d4f3c6681 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,6 +489,9 @@ endif() if(VELOX_ENABLE_WAVE OR VELOX_ENABLE_CUDF) enable_language(CUDA) + # Use same C++ standard throughout + set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) + set(CMAKE_CUDA_STANDARD_REQUIRED ${CMAKE_CXX_STANDARD_REQUIRED}) # Determine CUDA_ARCHITECTURES automatically. cmake_policy(SET CMP0104 NEW) if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) diff --git a/velox/experimental/cudf/CMakeLists.txt b/velox/experimental/cudf/CMakeLists.txt index fc476996efa..edbb5feeebe 100644 --- a/velox/experimental/cudf/CMakeLists.txt +++ b/velox/experimental/cudf/CMakeLists.txt @@ -12,10 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Build cuDF's CUDA sources at the same C++ standard as the rest of the project. -set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) -set(CMAKE_CUDA_STANDARD_REQUIRED ${CMAKE_CXX_STANDARD_REQUIRED}) - add_subdirectory(connectors) add_subdirectory(exec) add_subdirectory(expression) From 5680f4407fd3f12dfaf215d4d550f4b76441249b Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 9 Jun 2026 17:21:47 -0700 Subject: [PATCH 50/64] Use cuda::counting_iterator --- .../cudf/exec/DecimalAggregationDevice.cu | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index 2742654b057..a24c2b71171 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -24,10 +24,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -134,7 +134,7 @@ void launchFillOffsets( rmm::cuda_stream_view stream) { FillOffsetsFunctor op{offsets}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), + cuda::counting_iterator{0}, static_cast(offsets.size()), op, stream.value()); @@ -150,7 +150,7 @@ void launchPackState( rmm::cuda_stream_view stream) { PackStateFunctor op{sums, counts, offsets, chars}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), + cuda::counting_iterator{0}, static_cast(sums.size()), op, stream.value()); @@ -166,7 +166,7 @@ void launchUnpackState( rmm::cuda_stream_view stream) { UnpackStateFunctor op{offsets, chars, sums, counts}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), + cuda::counting_iterator{0}, static_cast(sums.size()), op, stream.value()); @@ -181,7 +181,7 @@ void launchAvgRound( rmm::cuda_stream_view stream) { AvgRoundFunctor op{sums, counts, out}; cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), + cuda::counting_iterator{0}, static_cast(out.size()), op, stream.value()); @@ -219,10 +219,11 @@ std::pair buildStateValidityMaskImpl( cudf::mask_state::UNALLOCATED, stream, mr); + auto iter = cuda::counting_iterator{0}; thrust::transform( rmm::exec_policy(stream), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(numRows), + iter, + iter + numRows, bools->mutable_view().begin(), pred); auto [mask, nullCount] = cudf::bools_to_mask(bools->view(), stream, mr); From e1a8cbfaa6cba1f2515442fea9432d1943ac797c Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 11 Jun 2026 10:38:06 -0700 Subject: [PATCH 51/64] Revert expression and CMake changes, moving to a Part 4 tidy-up --- CMakeLists.txt | 3 - .../cudf/expression/CMakeLists.txt | 4 +- .../expression/DecimalExpressionKernels.cpp | 172 --------- .../expression/DecimalExpressionKernels.cu | 333 ++++++++++++++++++ .../expression/DecimalExpressionKernels.h | 19 +- .../expression/DecimalExpressionKernelsGpu.cu | 223 ------------ .../expression/DecimalExpressionKernelsGpu.h | 62 ---- 7 files changed, 338 insertions(+), 478 deletions(-) create mode 100644 velox/experimental/cudf/expression/DecimalExpressionKernels.cu delete mode 100644 velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu delete mode 100644 velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b1d4f3c6681..b9d3b34b27b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,9 +489,6 @@ endif() if(VELOX_ENABLE_WAVE OR VELOX_ENABLE_CUDF) enable_language(CUDA) - # Use same C++ standard throughout - set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) - set(CMAKE_CUDA_STANDARD_REQUIRED ${CMAKE_CXX_STANDARD_REQUIRED}) # Determine CUDA_ARCHITECTURES automatically. cmake_policy(SET CMP0104 NEW) if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) diff --git a/velox/experimental/cudf/expression/CMakeLists.txt b/velox/experimental/cudf/expression/CMakeLists.txt index ab17b15a57e..2793101927a 100644 --- a/velox/experimental/cudf/expression/CMakeLists.txt +++ b/velox/experimental/cudf/expression/CMakeLists.txt @@ -18,7 +18,7 @@ add_library( AstExpression.cpp CommonFunctions.cpp DecimalExpressionKernels.cpp - DecimalExpressionKernelsGpu.cu + DecimalExpressionKernels.cu DecimalTypeCheck.cpp ExpressionEvaluator.cpp JitExpression.cpp @@ -34,3 +34,5 @@ target_link_libraries( ) target_compile_options(velox_cudf_expression PRIVATE -Wno-missing-field-initializers) + +set_target_properties(velox_cudf_expression PROPERTIES CUDA_STANDARD 20 CUDA_STANDARD_REQUIRED ON) diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp b/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp index a419bf5981d..84e4e72eb66 100644 --- a/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp +++ b/velox/experimental/cudf/expression/DecimalExpressionKernels.cpp @@ -15,49 +15,14 @@ */ #include "velox/experimental/cudf/expression/AstPrinter.h" #include "velox/experimental/cudf/expression/DecimalExpressionKernels.h" -#include "velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h" #include "velox/common/base/Exceptions.h" #include -#include #include -#include -#include #include -#include namespace facebook::velox::cudf_velox { -namespace { - -__int128_t getDecimalScalarValue( - const cudf::scalar& s, - rmm::cuda_stream_view stream) { - if (s.type().id() == cudf::type_id::DECIMAL64) { - auto const& dec = - static_cast const&>(s); - return static_cast<__int128_t>(static_cast(dec.value(stream))); - } - auto const& dec = - static_cast const&>(s); - return static_cast<__int128_t>(dec.value(stream)); -} - -/// Column of \p outputType with \p size rows, all null (e.g. NULL scalar -/// operand). -std::unique_ptr makeAllNullDecimalColumn( - cudf::data_type outputType, - cudf::size_type size, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - if (size == 0) { - return cudf::make_empty_column(outputType); - } - return cudf::make_fixed_width_column( - outputType, size, cudf::mask_state::ALL_NULL, stream, mr); -} - -} // namespace // Scatters null values to positions where the divisor is zero. // Returns a new column with nulls at zero-divisor positions. @@ -103,141 +68,4 @@ std::unique_ptr scatterNullsAtZeroDivisor( *nullScalar, *result, divisorIsZero->view(), stream, mr); } -std::unique_ptr decimalDivide( - const cudf::column_view& lhs, - const cudf::column_view& rhs, - cudf::data_type outputType, - int32_t aRescale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - VELOX_CHECK(lhs.size() == rhs.size(), "Decimal divide requires equal sizes"); - // Use VELOX_CHECK (not _EQ) so failed checks do not pass cudf::type_id into - // fmt, which has no formatter for that enum. - VELOX_CHECK( - lhs.type().id() == rhs.type().id(), - "Decimal divide requires matching input types"); - VELOX_CHECK_GE( - aRescale, 0, "Decimal divide requires non-negative rescale factor"); - - const auto inType = lhs.type().id(); - const auto outType = outputType.id(); - VELOX_CHECK( - inType == cudf::type_id::DECIMAL64 || inType == cudf::type_id::DECIMAL128, - "Unsupported input type for decimal divide"); - if (inType == cudf::type_id::DECIMAL64) { - VELOX_CHECK( - outType == cudf::type_id::DECIMAL64 || - outType == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - } else { - VELOX_CHECK( - outType == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - } - - // Combine input null masks (lhs and rhs nulls). - auto [nullMask, nullCount] = - cudf::bitmask_and(cudf::table_view({lhs, rhs}), stream, mr); - - // Create output column with input null mask and perform division. - auto out = cudf::make_fixed_width_column( - outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); - - detail::decimalDivideColumnColumn( - inType, outType, lhs, rhs, out->mutable_view(), aRescale, stream); - - // Scatter nulls where divisor is zero. - return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); -} - -std::unique_ptr decimalDivide( - const cudf::column_view& lhs, - const cudf::scalar& rhs, - cudf::data_type outputType, - int32_t aRescale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - VELOX_CHECK_GE( - aRescale, 0, "Decimal divide requires non-negative rescale factor"); - - if (!rhs.is_valid(stream)) { - return makeAllNullDecimalColumn(outputType, lhs.size(), stream, mr); - } - - auto nullMask = cudf::copy_bitmask(lhs, stream, mr); - auto nullCount = lhs.null_count(); - auto out = cudf::make_fixed_width_column( - outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); - - auto rhsValue = getDecimalScalarValue(rhs, stream); - - const auto inType = lhs.type().id(); - const auto outType = outputType.id(); - VELOX_CHECK( - inType == cudf::type_id::DECIMAL64 || inType == cudf::type_id::DECIMAL128, - "Unsupported input type for decimal divide"); - if (inType == cudf::type_id::DECIMAL64) { - VELOX_CHECK( - outType == cudf::type_id::DECIMAL64 || - outType == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - } else { - VELOX_CHECK( - outType == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - } - - detail::decimalDivideColumnScalar( - inType, outType, lhs, rhsValue, out->mutable_view(), aRescale, stream); - - return out; -} - -std::unique_ptr decimalDivide( - const cudf::scalar& lhs, - const cudf::column_view& rhs, - cudf::data_type outputType, - int32_t aRescale, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { - VELOX_CHECK_GE( - aRescale, 0, "Decimal divide requires non-negative rescale factor"); - - if (!lhs.is_valid(stream)) { - return makeAllNullDecimalColumn(outputType, rhs.size(), stream, mr); - } - - // Copy rhs null mask. - auto nullMask = cudf::copy_bitmask(rhs, stream, mr); - auto nullCount = rhs.null_count(); - - // Create output column and perform division. - auto out = cudf::make_fixed_width_column( - outputType, rhs.size(), std::move(nullMask), nullCount, stream, mr); - - auto lhsValue = getDecimalScalarValue(lhs, stream); - - const auto inType = rhs.type().id(); - const auto outType = outputType.id(); - VELOX_CHECK( - inType == cudf::type_id::DECIMAL64 || inType == cudf::type_id::DECIMAL128, - "Unsupported input type for decimal divide"); - if (inType == cudf::type_id::DECIMAL64) { - VELOX_CHECK( - outType == cudf::type_id::DECIMAL64 || - outType == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - } else { - VELOX_CHECK( - outType == cudf::type_id::DECIMAL128, - "Unexpected output type for decimal divide"); - } - - detail::decimalDivideScalarColumn( - inType, outType, lhsValue, rhs, out->mutable_view(), aRescale, stream); - - // Scatter nulls where divisor is zero. - return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); -} - } // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernels.cu b/velox/experimental/cudf/expression/DecimalExpressionKernels.cu new file mode 100644 index 00000000000..cd9291ff493 --- /dev/null +++ b/velox/experimental/cudf/expression/DecimalExpressionKernels.cu @@ -0,0 +1,333 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/experimental/cudf/expression/DecimalExpressionKernels.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace facebook::velox::cudf_velox { +namespace { + +template +__device__ OutT +decimalDivideImpl(__int128_t numerator, __int128_t denom, __int128_t scale) { + if (denom == 0) { + return OutT{0}; + } + int sign = 1; + if (numerator < 0) { + numerator = -numerator; + sign = -sign; + } + if (denom < 0) { + denom = -denom; + sign = -sign; + } + __int128_t scaled = numerator * scale; + __int128_t quotient = scaled / denom; + __int128_t remainder = scaled % denom; + if (remainder * 2 >= denom) { + ++quotient; + } + if (sign < 0) { + quotient = -quotient; + } + return static_cast(quotient); +} + +inline __int128_t pow10Int128(int32_t exp) { + __int128_t value = 1; + for (int32_t i = 0; i < exp; ++i) { + value *= 10; + } + return value; +} + +template +struct DivideFunctor { + const InT* lhs; + const InT* rhs; + OutT* out; + __int128_t scale; + + __device__ void operator()(int32_t idx) const { + out[idx] = decimalDivideImpl(lhs[idx], rhs[idx], scale); + } +}; + +template +struct DivideLhsScalarFunctor { + __int128_t lhsValue; + const InColT* rhs; + OutT* out; + __int128_t scale; + + __device__ void operator()(int32_t idx) const { + out[idx] = decimalDivideImpl(lhsValue, rhs[idx], scale); + } +}; + +template +struct DivideRhsScalarFunctor { + const InColT* lhs; + __int128_t rhsValue; + OutT* out; + __int128_t scale; + + __device__ void operator()(int32_t idx) const { + out[idx] = decimalDivideImpl(lhs[idx], rhsValue, scale); + } +}; + +template +void launchDivideKernel( + const cudf::column_view& lhs, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (lhs.size() == 0) { + return; + } + DivideFunctor op{ + lhs.data(), + rhs.data(), + out.data(), + pow10Int128(aRescale)}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), lhs.size(), op, stream.value()); +} + +template +void launchDivideKernelLhsScalar( + __int128_t lhsValue, + const cudf::column_view& rhs, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (rhs.size() == 0) { + return; + } + DivideLhsScalarFunctor op{ + lhsValue, rhs.data(), out.data(), pow10Int128(aRescale)}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), rhs.size(), op, stream.value()); +} + +template +void launchDivideKernelRhsScalar( + const cudf::column_view& lhs, + __int128_t rhsValue, + cudf::mutable_column_view out, + int32_t aRescale, + rmm::cuda_stream_view stream) { + if (lhs.size() == 0) { + return; + } + DivideRhsScalarFunctor op{ + lhs.data(), rhsValue, out.data(), pow10Int128(aRescale)}; + cub::DeviceFor::ForEachN( + thrust::counting_iterator(0), lhs.size(), op, stream.value()); +} + +__int128_t getDecimalScalarValue( + const cudf::scalar& s, + rmm::cuda_stream_view stream) { + if (s.type().id() == cudf::type_id::DECIMAL64) { + auto const& dec = + static_cast const&>(s); + return static_cast<__int128_t>(static_cast(dec.value(stream))); + } + auto const& dec = + static_cast const&>(s); + return static_cast<__int128_t>(dec.value(stream)); +} + +/// Column of \p outputType with \p size rows, all null (e.g. NULL scalar +/// operand). +std::unique_ptr makeAllNullDecimalColumn( + cudf::data_type outputType, + cudf::size_type size, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + if (size == 0) { + return cudf::make_empty_column(outputType); + } + return cudf::make_fixed_width_column( + outputType, size, cudf::mask_state::ALL_NULL, stream, mr); +} + +} // namespace + +std::unique_ptr decimalDivide( + const cudf::column_view& lhs, + const cudf::column_view& rhs, + cudf::data_type outputType, + int32_t aRescale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + CUDF_EXPECTS(lhs.size() == rhs.size(), "Decimal divide requires equal sizes"); + CUDF_EXPECTS( + lhs.type().id() == rhs.type().id(), + "Decimal divide requires matching input types"); + CUDF_EXPECTS( + aRescale >= 0, "Decimal divide requires non-negative rescale factor"); + + // Combine input null masks (lhs and rhs nulls). + auto [nullMask, nullCount] = + cudf::bitmask_and(cudf::table_view({lhs, rhs}), stream, mr); + + // Create output column with input null mask and perform division. + auto out = cudf::make_fixed_width_column( + outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); + + if (lhs.type().id() == cudf::type_id::DECIMAL64) { + if (outputType.id() == cudf::type_id::DECIMAL64) { + launchDivideKernel( + lhs, rhs, out->mutable_view(), aRescale, stream); + } else { + CUDF_EXPECTS( + outputType.id() == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + launchDivideKernel( + lhs, rhs, out->mutable_view(), aRescale, stream); + } + } else { + CUDF_EXPECTS( + lhs.type().id() == cudf::type_id::DECIMAL128, + "Unsupported input type for decimal divide"); + CUDF_EXPECTS( + outputType.id() == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + launchDivideKernel<__int128_t, __int128_t>( + lhs, rhs, out->mutable_view(), aRescale, stream); + } + + // Scatter nulls where divisor is zero. + return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); +} + +std::unique_ptr decimalDivide( + const cudf::column_view& lhs, + const cudf::scalar& rhs, + cudf::data_type outputType, + int32_t aRescale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + CUDF_EXPECTS( + aRescale >= 0, "Decimal divide requires non-negative rescale factor"); + + if (!rhs.is_valid(stream)) { + return makeAllNullDecimalColumn(outputType, lhs.size(), stream, mr); + } + + auto nullMask = cudf::copy_bitmask(lhs, stream, mr); + auto nullCount = lhs.null_count(); + auto out = cudf::make_fixed_width_column( + outputType, lhs.size(), std::move(nullMask), nullCount, stream, mr); + + auto rhsValue = getDecimalScalarValue(rhs, stream); + + if (lhs.type().id() == cudf::type_id::DECIMAL64) { + if (outputType.id() == cudf::type_id::DECIMAL64) { + launchDivideKernelRhsScalar( + lhs, rhsValue, out->mutable_view(), aRescale, stream); + } else { + CUDF_EXPECTS( + outputType.id() == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + launchDivideKernelRhsScalar( + lhs, rhsValue, out->mutable_view(), aRescale, stream); + } + } else { + CUDF_EXPECTS( + lhs.type().id() == cudf::type_id::DECIMAL128, + "Unsupported input type for decimal divide"); + CUDF_EXPECTS( + outputType.id() == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + launchDivideKernelRhsScalar<__int128_t, __int128_t>( + lhs, rhsValue, out->mutable_view(), aRescale, stream); + } + + return out; +} + +std::unique_ptr decimalDivide( + const cudf::scalar& lhs, + const cudf::column_view& rhs, + cudf::data_type outputType, + int32_t aRescale, + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + CUDF_EXPECTS( + aRescale >= 0, "Decimal divide requires non-negative rescale factor"); + + if (!lhs.is_valid(stream)) { + return makeAllNullDecimalColumn(outputType, rhs.size(), stream, mr); + } + + // Copy rhs null mask. + auto nullMask = cudf::copy_bitmask(rhs, stream, mr); + auto nullCount = rhs.null_count(); + + // Create output column and perform division. + auto out = cudf::make_fixed_width_column( + outputType, rhs.size(), std::move(nullMask), nullCount, stream, mr); + + auto lhsValue = getDecimalScalarValue(lhs, stream); + + if (rhs.type().id() == cudf::type_id::DECIMAL64) { + if (outputType.id() == cudf::type_id::DECIMAL64) { + launchDivideKernelLhsScalar( + lhsValue, rhs, out->mutable_view(), aRescale, stream); + } else { + CUDF_EXPECTS( + outputType.id() == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + launchDivideKernelLhsScalar( + lhsValue, rhs, out->mutable_view(), aRescale, stream); + } + } else { + CUDF_EXPECTS( + rhs.type().id() == cudf::type_id::DECIMAL128, + "Unsupported input type for decimal divide"); + CUDF_EXPECTS( + outputType.id() == cudf::type_id::DECIMAL128, + "Unexpected output type for decimal divide"); + launchDivideKernelLhsScalar<__int128_t, __int128_t>( + lhsValue, rhs, out->mutable_view(), aRescale, stream); + } + + // Scatter nulls where divisor is zero. + return scatterNullsAtZeroDivisor(std::move(out), rhs, stream, mr); +} + +} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernels.h b/velox/experimental/cudf/expression/DecimalExpressionKernels.h index 46b95d05cc5..d7576ccdcf0 100644 --- a/velox/experimental/cudf/expression/DecimalExpressionKernels.h +++ b/velox/experimental/cudf/expression/DecimalExpressionKernels.h @@ -26,12 +26,6 @@ namespace facebook::velox::cudf_velox { -// Element-wise decimal division of two columns (same DECIMAL64 or DECIMAL128 -// input type). Builds the output null mask as the bitwise AND of lhs and rhs -// validity, runs the GPU divide into outputType, and applies -// scatterNullsAtZeroDivisor so rows with a zero divisor are null. aRescale is -// the fixed-point scale adjustment (Velox passes outScale - lhsScale + -// rhsScale) used inside the kernel as a power-of-ten factor. std::unique_ptr decimalDivide( const cudf::column_view& lhs, const cudf::column_view& rhs, @@ -40,9 +34,6 @@ std::unique_ptr decimalDivide( rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); -// Like column/column decimalDivide, but rhs is a single decimal scalar. If the -// scalar is invalid, returns an all-null column of outputType; otherwise copies -// lhs nulls and divides without zero-divisor scattering (rhs is not per-row). std::unique_ptr decimalDivide( const cudf::column_view& lhs, const cudf::scalar& rhs, @@ -51,10 +42,6 @@ std::unique_ptr decimalDivide( rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); -// Like column/column decimalDivide, but lhs is a scalar and rhs is a column. -// Invalid lhs yields all-null output; otherwise rhs nulls are propagated, then -// divide and scatterNullsAtZeroDivisor on rhs so division-by-zero rows are -// null. std::unique_ptr decimalDivide( const cudf::scalar& lhs, const cudf::column_view& rhs, @@ -63,10 +50,8 @@ std::unique_ptr decimalDivide( rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); -// After a decimal divide, forces output rows to null where the divisor column -// compares equal to zero (DECIMAL64 or DECIMAL128), using copy_if_else. Kept in -// the .cpp translation unit so it can use Velox checks alongside cuDF APIs -// without pulling those into CUDA compilation units. +// Helper function to scatter nulls at zero-divisor positions. +// Moved to .cpp file to allow use of VELOX_FAIL (incompatible with nvcc). std::unique_ptr scatterNullsAtZeroDivisor( std::unique_ptr result, const cudf::column_view& divisor, diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu deleted file mode 100644 index 29b7c13f59d..00000000000 --- a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.cu +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * 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 "velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h" - -#include - -#include -#include -#include - -#include - -namespace facebook::velox::cudf_velox { -namespace { - -template -__device__ OutT -decimalDivideImpl(__int128_t numerator, __int128_t denom, __int128_t scale) { - if (denom == 0) { - return OutT{0}; - } - int sign = 1; - if (numerator < 0) { - numerator = -numerator; - sign = -sign; - } - if (denom < 0) { - denom = -denom; - sign = -sign; - } - __int128_t scaled = numerator * scale; - __int128_t quotient = scaled / denom; - __int128_t remainder = scaled % denom; - if (remainder * 2 >= denom) { - ++quotient; - } - if (sign < 0) { - quotient = -quotient; - } - return static_cast(quotient); -} - -inline __int128_t pow10Int128(int32_t exp) { - __int128_t value = 1; - for (int32_t i = 0; i < exp; ++i) { - value *= 10; - } - return value; -} - -template -struct DivideFunctor { - const InT* lhs; - const InT* rhs; - OutT* out; - __int128_t scale; - - __device__ void operator()(int32_t idx) const { - out[idx] = decimalDivideImpl(lhs[idx], rhs[idx], scale); - } -}; - -template -struct DivideLhsScalarFunctor { - __int128_t lhsValue; - const InColT* rhs; - OutT* out; - __int128_t scale; - - __device__ void operator()(int32_t idx) const { - out[idx] = decimalDivideImpl(lhsValue, rhs[idx], scale); - } -}; - -template -struct DivideRhsScalarFunctor { - const InColT* lhs; - __int128_t rhsValue; - OutT* out; - __int128_t scale; - - __device__ void operator()(int32_t idx) const { - out[idx] = decimalDivideImpl(lhs[idx], rhsValue, scale); - } -}; - -template -void launchDivideKernel( - const cudf::column_view& lhs, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (lhs.size() == 0) { - return; - } - DivideFunctor op{ - lhs.data(), - rhs.data(), - out.data(), - pow10Int128(aRescale)}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), lhs.size(), op, stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchDivideKernelLhsScalar( - __int128_t lhsValue, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (rhs.size() == 0) { - return; - } - DivideLhsScalarFunctor op{ - lhsValue, rhs.data(), out.data(), pow10Int128(aRescale)}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), rhs.size(), op, stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchDivideKernelRhsScalar( - const cudf::column_view& lhs, - __int128_t rhsValue, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (lhs.size() == 0) { - return; - } - DivideRhsScalarFunctor op{ - lhs.data(), rhsValue, out.data(), pow10Int128(aRescale)}; - cub::DeviceFor::ForEachN( - thrust::counting_iterator(0), lhs.size(), op, stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -} // namespace - -namespace detail { - -void decimalDivideColumnColumn( - cudf::type_id inType, - cudf::type_id outType, - const cudf::column_view& lhs, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (inType == cudf::type_id::DECIMAL64) { - if (outType == cudf::type_id::DECIMAL64) { - launchDivideKernel(lhs, rhs, out, aRescale, stream); - } else { - launchDivideKernel(lhs, rhs, out, aRescale, stream); - } - } else { - launchDivideKernel<__int128_t, __int128_t>(lhs, rhs, out, aRescale, stream); - } -} - -void decimalDivideColumnScalar( - cudf::type_id inType, - cudf::type_id outType, - const cudf::column_view& lhs, - __int128_t rhsValue, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (inType == cudf::type_id::DECIMAL64) { - if (outType == cudf::type_id::DECIMAL64) { - launchDivideKernelRhsScalar( - lhs, rhsValue, out, aRescale, stream); - } else { - launchDivideKernelRhsScalar( - lhs, rhsValue, out, aRescale, stream); - } - } else { - launchDivideKernelRhsScalar<__int128_t, __int128_t>( - lhs, rhsValue, out, aRescale, stream); - } -} - -void decimalDivideScalarColumn( - cudf::type_id inType, - cudf::type_id outType, - __int128_t lhsValue, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream) { - if (inType == cudf::type_id::DECIMAL64) { - if (outType == cudf::type_id::DECIMAL64) { - launchDivideKernelLhsScalar( - lhsValue, rhs, out, aRescale, stream); - } else { - launchDivideKernelLhsScalar( - lhsValue, rhs, out, aRescale, stream); - } - } else { - launchDivideKernelLhsScalar<__int128_t, __int128_t>( - lhsValue, rhs, out, aRescale, stream); - } -} - -} // namespace detail -} // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h b/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h deleted file mode 100644 index 310be3ee834..00000000000 --- a/velox/experimental/cudf/expression/DecimalExpressionKernelsGpu.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * 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 -#include - -#include - -#include - -namespace facebook::velox::cudf_velox::detail { - -// Dispatches a per-row device loop: fixed-point divide (lhs * 10^aRescale) / -// rhs with half-away-from-zero rounding on the remainder, writing into out. -// Zero divisors produce a numeric zero in out (callers patch nulls). inType / -// outType select DECIMAL64 vs DECIMAL128 storage widths for inputs and result. -void decimalDivideColumnColumn( - cudf::type_id inType, - cudf::type_id outType, - const cudf::column_view& lhs, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream); - -// Same kernel math as decimalDivideColumnColumn, but rhs is a single -// __int128_t decimal payload (already decoded from a cuDF scalar). -void decimalDivideColumnScalar( - cudf::type_id inType, - cudf::type_id outType, - const cudf::column_view& lhs, - __int128_t rhsValue, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream); - -// Same kernel math as decimalDivideColumnColumn, but lhs is a single -// __int128_t decimal payload and rhs is per-row. -void decimalDivideScalarColumn( - cudf::type_id inType, - cudf::type_id outType, - __int128_t lhsValue, - const cudf::column_view& rhs, - cudf::mutable_column_view out, - int32_t aRescale, - rmm::cuda_stream_view stream); - -} // namespace facebook::velox::cudf_velox::detail From afbda570beb958dca9eac1b0f3ed389b364c527d Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 11 Jun 2026 13:10:21 -0700 Subject: [PATCH 52/64] Final get_output_mr() plumbing, per @bdice and @karthikeyann --- velox/experimental/cudf/exec/CudfGroupby.cpp | 100 ++++++----- velox/experimental/cudf/exec/CudfGroupby.h | 6 +- velox/experimental/cudf/exec/CudfReduce.cpp | 157 ++++++++++-------- velox/experimental/cudf/exec/CudfReduce.h | 6 +- .../cudf/exec/DecimalAggregationHostOps.cpp | 21 ++- .../cudf/exec/DecimalAggregationHostOps.h | 9 +- .../cudf/exec/DecimalAggregationState.cpp | 16 +- .../cudf/exec/DecimalAggregationState.h | 6 +- .../cudf/tests/DecimalAggregationTest.cpp | 42 +++-- 9 files changed, 214 insertions(+), 149 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 7cfcc8e80fd..b905178c037 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -76,11 +76,12 @@ using cudf_velox::validateIntermediateColumnType; \ std::unique_ptr makeOutputColumn( \ std::vector& results, \ - rmm::cuda_stream_view stream) override { \ + rmm::cuda_stream_view stream, \ + rmm::device_async_resource_ref mr) override { \ auto col = std::move(results[output_idx].results[0]); \ const auto cudfType = cudf_velox::veloxToCudfDataType(resultType); \ if (col->type() != cudfType) { \ - col = cudf::cast(*col, cudfType, stream, get_output_mr()); \ + col = cudf::cast(*col, cudfType, stream, mr); \ } \ return col; \ } \ @@ -240,21 +241,22 @@ struct GroupbyDecimalSumAggregator : GroupbyAggregator { std::unique_ptr makeOutputColumn( std::vector& results, - rmm::cuda_stream_view stream) override { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) override { auto col = std::move(results[sumIdx_].results[0]); if (step == core::AggregationNode::Step::kPartial) { auto count = std::move(results[sumIdx_].results[1]); return serializeDecimalPartialOrIntermediateState( - std::move(col), std::move(count), stream); + std::move(col), std::move(count), stream, mr); } if (step == core::AggregationNode::Step::kIntermediate) { auto count = std::move(results[countIdx_].results[0]); return serializeDecimalPartialOrIntermediateState( - std::move(col), std::move(count), stream); + std::move(col), std::move(count), stream, mr); } auto const cudfResType = cudf_velox::veloxToCudfDataType(resultType); if (col->type() != cudfResType) { - col = cudf::cast(*col, cudfResType, stream, cudf_velox::get_output_mr()); + col = cudf::cast(*col, cudfResType, stream, mr); } return col; } @@ -318,27 +320,28 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { std::unique_ptr makeOutputColumn( std::vector& results, - rmm::cuda_stream_view stream) override { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) override { auto col = std::move(results[sumIdx_].results[0]); if (step == core::AggregationNode::Step::kSingle) { auto count = std::move(results[sumIdx_].results[1]); return finalizeDecimalAverage( - std::move(col), std::move(count), resultType, stream); + std::move(col), std::move(count), resultType, stream, mr); } if (step == core::AggregationNode::Step::kPartial) { auto count = std::move(results[sumIdx_].results[1]); return serializeDecimalPartialOrIntermediateState( - std::move(col), std::move(count), stream); + std::move(col), std::move(count), stream, mr); } if (step == core::AggregationNode::Step::kIntermediate) { auto count = std::move(results[countIdx_].results[0]); return serializeDecimalPartialOrIntermediateState( - std::move(col), std::move(count), stream); + std::move(col), std::move(count), stream, mr); } if (step == core::AggregationNode::Step::kFinal) { auto count = std::move(results[countIdx_].results[0]); return finalizeDecimalAverage( - std::move(col), std::move(count), resultType, stream); + std::move(col), std::move(count), resultType, stream, mr); } // All four aggregation steps are handled above. VELOX_UNREACHABLE(); @@ -389,17 +392,18 @@ struct GroupbyCountAggregator : GroupbyAggregator { std::unique_ptr makeOutputColumn( std::vector& results, - rmm::cuda_stream_view stream) override { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) override { auto col = std::move(results[outputIndex_].results[0]); if (inputKind_ == CountInputKind::kNullConstant) { auto zero = cudf::numeric_scalar(0, true, stream, get_temp_mr()); col = cudf::make_column_from_scalar( - zero, col->size(), stream, get_output_mr()); + zero, col->size(), stream, mr); } // cudf produces int32 for count but velox expects int64. const auto cudfOutputType = cudf_velox::veloxToCudfDataType(resultType); if (col->type() != cudfOutputType) { - col = cudf::cast(*col, cudfOutputType, stream, get_output_mr()); + col = cudf::cast(*col, cudfOutputType, stream, mr); } return col; } @@ -467,7 +471,8 @@ struct GroupbyMeanAggregator : GroupbyAggregator { std::unique_ptr makeOutputColumn( std::vector& results, - rmm::cuda_stream_view stream) override { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) override { const auto& outputType = asRowType(resultType); switch (step) { case core::AggregationNode::Step::kSingle: @@ -483,11 +488,11 @@ struct GroupbyMeanAggregator : GroupbyAggregator { cudf_velox::veloxToCudfDataType(outputType->childAt(1)); if (sum->type() != cudf::data_type(cudfSumType)) { sum = cudf::cast( - *sum, cudf::data_type(cudfSumType), stream, get_output_mr()); + *sum, cudf::data_type(cudfSumType), stream, mr); } if (count->type() != cudf::data_type(cudfCountType)) { count = cudf::cast( - *count, cudf::data_type(cudfCountType), stream, get_output_mr()); + *count, cudf::data_type(cudfCountType), stream, mr); } auto children = std::vector>(); @@ -521,11 +526,11 @@ struct GroupbyMeanAggregator : GroupbyAggregator { cudf_velox::veloxToCudfDataType(outputType->childAt(1)); if (sum->type() != cudf::data_type(cudfSumType)) { sum = cudf::cast( - *sum, cudf::data_type(cudfSumType), stream, get_output_mr()); + *sum, cudf::data_type(cudfSumType), stream, mr); } if (count->type() != cudf::data_type(cudfCountType)) { count = cudf::cast( - *count, cudf::data_type(cudfCountType), stream, get_output_mr()); + *count, cudf::data_type(cudfCountType), stream, mr); } auto children = std::vector>(); @@ -549,7 +554,7 @@ struct GroupbyMeanAggregator : GroupbyAggregator { cudf::binary_operator::DIV, cudf_velox::veloxToCudfDataType(resultType), stream, - get_output_mr()); + mr); return avg; } default: @@ -610,7 +615,8 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { std::unique_ptr makeOutputColumn( std::vector& results, - rmm::cuda_stream_view stream) override { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) override { switch (step) { case core::AggregationNode::Step::kSingle: return std::move(results[outputIdx_].results[0]); @@ -619,7 +625,7 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { auto mean = std::move(results[outputIdx_].results[1]); auto m2 = std::move(results[outputIdx_].results[2]); return makeM2StructColumn( - std::move(count), std::move(mean), std::move(m2), stream); + std::move(count), std::move(mean), std::move(m2), stream, mr); } case core::AggregationNode::Step::kIntermediate: { auto merged = std::move(results[outputIdx_].results[0]); @@ -646,13 +652,13 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { // Types don't match - need to copy and cast (use output_mr since // these become part of the output) auto count = std::make_unique( - mergedView.child(0), stream, get_output_mr()); + mergedView.child(0), stream, mr); auto mean = std::make_unique( - mergedView.child(1), stream, get_output_mr()); + mergedView.child(1), stream, mr); auto m2 = std::make_unique( - mergedView.child(2), stream, get_output_mr()); + mergedView.child(2), stream, mr); return makeM2StructColumn( - std::move(count), std::move(mean), std::move(m2), stream); + std::move(count), std::move(mean), std::move(m2), stream, mr); } case core::AggregationNode::Step::kFinal: { // MERGE_M2 returns struct(count, mean, m2) @@ -699,7 +705,7 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { cudf::numeric_scalar nullDouble( 0.0, false, stream, get_temp_mr()); return cudf::copy_if_else( - *stddev, nullDouble, *validMask, stream, get_output_mr()); + *stddev, nullDouble, *validMask, stream, mr); } default: VELOX_NYI("Unsupported aggregation step for stddev_samp"); @@ -712,7 +718,8 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { std::unique_ptr count, std::unique_ptr mean, std::unique_ptr m2, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { const auto& outputType = asRowType(resultType); auto const cudfCountType = cudf_velox::veloxToCudfDataType(outputType->childAt(0)); @@ -721,13 +728,13 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { auto const cudfM2Type = cudf_velox::veloxToCudfDataType(outputType->childAt(2)); if (count->type() != cudfCountType) { - count = cudf::cast(*count, cudfCountType, stream, get_output_mr()); + count = cudf::cast(*count, cudfCountType, stream, mr); } if (mean->type() != cudfMeanType) { - mean = cudf::cast(*mean, cudfMeanType, stream, get_output_mr()); + mean = cudf::cast(*mean, cudfMeanType, stream, mr); } if (m2->type() != cudfM2Type) { - m2 = cudf::cast(*m2, cudfM2Type, stream, get_output_mr()); + m2 = cudf::cast(*m2, cudfM2Type, stream, mr); } auto const size = count->size(); @@ -979,7 +986,8 @@ void CudfGroupby::computePartialGroupbyStreaming(CudfVectorPtr tbl) { groupingKeyOutputChannels_, aggregators_, bufferedResultType_, - inputTableStream); + inputTableStream, + get_output_mr()); // If we already have partial output, concatenate the new results with it. if (bufferedResult_) { @@ -1000,7 +1008,8 @@ void CudfGroupby::computePartialGroupbyStreaming(CudfVectorPtr tbl) { groupingKeyOutputChannels_, intermediateAggregators_, bufferedResultType_, - partialOutputStream); + partialOutputStream, + get_output_mr()); bufferedResult_ = compactedOutput; } else { // First time processing, just store the result of the input batch's groupby @@ -1020,7 +1029,8 @@ void CudfGroupby::computeFinalGroupbyStreaming(CudfVectorPtr tbl) { groupingKeyOutputChannels_, intermediateAggregators_, bufferedResultType_, - inputTableStream); + inputTableStream, + get_output_mr()); if (!groupbyOnInput) { return; } @@ -1045,7 +1055,8 @@ void CudfGroupby::computeFinalGroupbyStreaming(CudfVectorPtr tbl) { groupingKeyOutputChannels_, intermediateAggregators_, bufferedResultType_, - finalStream); + finalStream, + get_output_mr()); bufferedResult_ = compactedOutput; } @@ -1058,7 +1069,8 @@ void CudfGroupby::computeSingleGroupbyStreaming(CudfVectorPtr tbl) { groupingKeyOutputChannels_, partialAggregators_, bufferedResultType_, - inputTableStream); + inputTableStream, + get_output_mr()); if (bufferedResult_) { auto partialOutputStream = bufferedResult_->stream(); @@ -1076,7 +1088,8 @@ void CudfGroupby::computeSingleGroupbyStreaming(CudfVectorPtr tbl) { groupingKeyOutputChannels_, intermediateAggregators_, bufferedResultType_, - partialOutputStream); + partialOutputStream, + get_output_mr()); bufferedResult_ = compactedOutput; } else { bufferedResult_ = groupbyOnInput; @@ -1114,7 +1127,8 @@ CudfVectorPtr CudfGroupby::doGroupByAggregation( std::vector const& groupByKeys, std::vector>& aggregators, TypePtr const& outputType, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { auto groupbyKeyView = tableView.select(groupByKeys.begin(), groupByKeys.end()); @@ -1131,7 +1145,7 @@ CudfVectorPtr CudfGroupby::doGroupByAggregation( } auto [groupKeys, results] = - groupByOwner.aggregate(requests, stream, get_output_mr()); + groupByOwner.aggregate(requests, stream, mr); // flatten the results std::vector> resultColumns; @@ -1144,7 +1158,7 @@ CudfVectorPtr CudfGroupby::doGroupByAggregation( // then fill the aggregation results for (auto& aggregator : aggregators) { - resultColumns.push_back(aggregator->makeOutputColumn(results, stream)); + resultColumns.push_back(aggregator->makeOutputColumn(results, stream, mr)); } // make a cudf table out of columns @@ -1228,7 +1242,8 @@ RowVectorPtr CudfGroupby::doGetOutput() { groupingKeyOutputChannels_, aggs, outputType_, - stream); + stream, + get_output_mr()); stream.synchronize(); bufferedResult_.reset(); return result; @@ -1260,7 +1275,8 @@ RowVectorPtr CudfGroupby::doGetOutput() { groupingKeyOutputChannels_, aggregators_, outputType_, - stream); + stream, + get_output_mr()); } void CudfGroupby::doNoMoreInput() { diff --git a/velox/experimental/cudf/exec/CudfGroupby.h b/velox/experimental/cudf/exec/CudfGroupby.h index 4c3f0acbb0a..0d946145b33 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.h +++ b/velox/experimental/cudf/exec/CudfGroupby.h @@ -35,7 +35,8 @@ struct GroupbyAggregator { virtual std::unique_ptr makeOutputColumn( std::vector& results, - rmm::cuda_stream_view stream) = 0; + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) = 0; virtual ~GroupbyAggregator() = default; @@ -101,7 +102,8 @@ class CudfGroupby : public CudfOperatorBase { std::vector const& groupByKeys, std::vector>& aggregators, TypePtr const& outputType, - rmm::cuda_stream_view stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); CudfVectorPtr releaseAndResetBufferedResult(); diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 05b638edcdc..0c529ca89bb 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -64,8 +64,9 @@ using facebook::velox::cudf_velox::validateIntermediateColumnType; std::unique_ptr doReduce( \ cudf::table_view const& input, \ TypePtr const& outputType, \ + vector_size_t /* inputRowCount */, \ rmm::cuda_stream_view stream, \ - vector_size_t /*inputRowCount*/) override { \ + rmm::device_async_resource_ref mr) override { \ auto const aggRequest = \ cudf::make_##name##_aggregation(); \ auto const cudfOutputType = cudf_velox::veloxToCudfDataType(outputType); \ @@ -76,7 +77,7 @@ using facebook::velox::cudf_velox::validateIntermediateColumnType; stream, \ get_temp_mr()); \ return cudf::make_column_from_scalar( \ - *resultScalar, 1, stream, get_output_mr()); \ + *resultScalar, 1, stream, mr); \ } \ }; @@ -96,8 +97,9 @@ struct ReduceCountAggregator : ReduceAggregator { std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, + vector_size_t inputRowCount, rmm::cuda_stream_view stream, - vector_size_t inputRowCount) override { + rmm::device_async_resource_ref mr) override { if (exec::isRawInput(step)) { int64_t count; switch (inputKind_) { @@ -124,7 +126,7 @@ struct ReduceCountAggregator : ReduceAggregator { cudf::numeric_scalar(count, true, stream, get_temp_mr()); return cudf::make_column_from_scalar( - resultScalar, 1, stream, get_output_mr()); + resultScalar, 1, stream, mr); } else { // For non-raw input (intermediate/final), use sum aggregation auto const aggRequest = @@ -138,7 +140,7 @@ struct ReduceCountAggregator : ReduceAggregator { get_temp_mr()); resultScalar->set_valid_async(true, stream); return cudf::make_column_from_scalar( - *resultScalar, 1, stream, get_output_mr()); + *resultScalar, 1, stream, mr); } } @@ -157,8 +159,9 @@ struct ReduceMeanAggregator : ReduceAggregator { std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, + vector_size_t /* inputRowCount */, rmm::cuda_stream_view stream, - vector_size_t /*inputRowCount*/) override { + rmm::device_async_resource_ref mr) override { switch (step) { case core::AggregationNode::Step::kSingle: { auto const aggRequest = @@ -171,7 +174,7 @@ struct ReduceMeanAggregator : ReduceAggregator { stream, get_temp_mr()); return cudf::make_column_from_scalar( - *resultScalar, 1, stream, get_output_mr()); + *resultScalar, 1, stream, mr); } case core::AggregationNode::Step::kPartial: { VELOX_CHECK(outputType->isRow()); @@ -191,7 +194,7 @@ struct ReduceMeanAggregator : ReduceAggregator { stream, get_temp_mr()); auto sumCol = cudf::make_column_from_scalar( - *sumResultScalar, 1, stream, get_output_mr()); + *sumResultScalar, 1, stream, mr); // libcudf doesn't have a count agg for reduce. What we want is to // count the number of valid rows. @@ -204,7 +207,7 @@ struct ReduceMeanAggregator : ReduceAggregator { get_temp_mr()), 1, stream, - get_output_mr()); + mr); // Assemble into struct as expected by velox. auto children = std::vector>(); @@ -229,7 +232,7 @@ struct ReduceMeanAggregator : ReduceAggregator { auto const sumResultScalar = cudf::reduce( sumCol, *sumAggRequest, sumCol.type(), stream, get_temp_mr()); auto sumResultCol = cudf::make_column_from_scalar( - *sumResultScalar, 1, stream, get_output_mr()); + *sumResultScalar, 1, stream, mr); // sum the counts auto const countAggRequest = @@ -245,7 +248,7 @@ struct ReduceMeanAggregator : ReduceAggregator { cudf::binary_operator::DIV, cudfOutputType, stream, - get_output_mr()); + mr); } default: VELOX_NYI("Unsupported aggregation step for mean"); @@ -257,17 +260,19 @@ struct ReduceMeanAggregator : ReduceAggregator { cudf_velox::DecimalSumStateColumns makeSumCountColumns( cudf::scalar const& sumScalar, cudf::scalar const& countScalar, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { cudf_velox::DecimalSumStateColumns cols; - cols.sum = cudf::make_column_from_scalar(sumScalar, 1, stream, get_temp_mr()); + cols.sum = cudf::make_column_from_scalar(sumScalar, 1, stream, mr); cols.count = - cudf::make_column_from_scalar(countScalar, 1, stream, get_temp_mr()); + cudf::make_column_from_scalar(countScalar, 1, stream, mr); return cols; } std::unique_ptr partialDecimalSumCountToSerializedString( cudf::column_view inputCol, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { std::unique_ptr castedInput; inputCol = castDecimal64InputToDecimal128(inputCol, castedInput, stream); auto const sumAgg = cudf::make_sum_aggregation(); @@ -281,9 +286,9 @@ std::unique_ptr partialDecimalSumCountToSerializedString( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - auto cols = makeSumCountColumns(*sumScalar, *countScalar, stream); + auto cols = makeSumCountColumns(*sumScalar, *countScalar, stream, mr); return serializeDecimalPartialOrIntermediateState( - std::move(cols.sum), std::move(cols.count), stream); + std::move(cols.sum), std::move(cols.count), stream, mr); } // Decodes serialized decimal SUM state, sums the per-row partial sums and @@ -294,7 +299,8 @@ std::unique_ptr partialDecimalSumCountToSerializedString( cudf_velox::DecimalSumStateColumns mergeSerializedDecimalSumState( cudf::column_view inputCol, int32_t scale, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { auto const sumAgg = cudf::make_sum_aggregation(); auto sumAndCount = cudf_velox::deserializeDecimalSumState(inputCol, scale, stream); @@ -303,39 +309,42 @@ cudf_velox::DecimalSumStateColumns mergeSerializedDecimalSumState( *sumAgg, sumAndCount.sum->view().type(), stream, - get_temp_mr()); + mr); auto countScalar = cudf::reduce( sumAndCount.count->view(), *sumAgg, cudf::data_type{cudf::type_id::INT64}, stream, - get_temp_mr()); - return makeSumCountColumns(*sumScalar, *countScalar, stream); + mr); + return makeSumCountColumns(*sumScalar, *countScalar, stream, mr); } std::unique_ptr intermediateDecimalMergeSerializedString( cudf::column_view inputCol, int32_t scale, - rmm::cuda_stream_view stream) { - auto merged = mergeSerializedDecimalSumState(inputCol, scale, stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + auto merged = mergeSerializedDecimalSumState(inputCol, scale, stream, mr); return serializeDecimalPartialOrIntermediateState( - std::move(merged.sum), std::move(merged.count), stream); + std::move(merged.sum), std::move(merged.count), stream, mr); } std::unique_ptr finalDecimalAvgFromSerializedString( cudf::column_view inputCol, int32_t scale, TypePtr const& resultType, - rmm::cuda_stream_view stream) { - auto merged = mergeSerializedDecimalSumState(inputCol, scale, stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + auto merged = mergeSerializedDecimalSumState(inputCol, scale, stream, mr); return finalizeDecimalAverage( - std::move(merged.sum), std::move(merged.count), resultType, stream); + std::move(merged.sum), std::move(merged.count), resultType, stream, mr); } std::unique_ptr singleDecimalAvgFromRawColumn( cudf::column_view inputCol, TypePtr const& resultType, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { std::unique_ptr castedInput; inputCol = castDecimal64InputToDecimal128(inputCol, castedInput, stream); auto const sumAgg = cudf::make_sum_aggregation(); @@ -349,15 +358,16 @@ std::unique_ptr singleDecimalAvgFromRawColumn( cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); - auto cols = makeSumCountColumns(*sumScalar, *countScalar, stream); + auto cols = makeSumCountColumns(*sumScalar, *countScalar, stream, mr); return finalizeDecimalAverage( - std::move(cols.sum), std::move(cols.count), resultType, stream); + std::move(cols.sum), std::move(cols.count), resultType, stream, mr); } std::unique_ptr singleOrRawDecimalSumWithCast( cudf::column_view inputCol, TypePtr const& outputType, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { auto const sumAgg = cudf::make_sum_aggregation(); auto const cudfOutType = cudf_velox::veloxToCudfDataType(outputType); std::unique_ptr castedInput; @@ -368,41 +378,44 @@ std::unique_ptr singleOrRawDecimalSumWithCast( auto const resultScalar = cudf::reduce(inputCol, *sumAgg, cudfOutType, stream, get_temp_mr()); return cudf::make_column_from_scalar( - *resultScalar, 1, stream, get_output_mr()); + *resultScalar, 1, stream, mr); } std::unique_ptr reduceIntermediateDecimalFromSerializedColumn( cudf::column_view inputCol, TypePtr const& outputType, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { validateIntermediateColumnType(inputCol); // outputType here could be DECIMAL or VARBINARY auto scale = outputType->isDecimal() ? getDecimalPrecisionScale(*outputType).second : 0; - return intermediateDecimalMergeSerializedString(inputCol, scale, stream); + return intermediateDecimalMergeSerializedString(inputCol, scale, stream, mr); } std::unique_ptr reduceFinalDecimalSumFromSerializedColumn( cudf::column_view inputCol, TypePtr const& outputType, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { validateIntermediateColumnType(inputCol); auto scale = getDecimalPrecisionScale(*outputType).second; auto sumAndCount = cudf_velox::deserializeDecimalSumState(inputCol, scale, stream); return singleOrRawDecimalSumWithCast( - sumAndCount.sum->view(), outputType, stream); + sumAndCount.sum->view(), outputType, stream, mr); } std::unique_ptr reduceFinalDecimalAvgFromSerializedColumn( cudf::column_view inputCol, TypePtr const& outputType, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { validateIntermediateColumnType(inputCol); auto scale = getDecimalPrecisionScale(*outputType).second; return finalDecimalAvgFromSerializedString( - inputCol, scale, outputType, stream); + inputCol, scale, outputType, stream, mr); } // Decimal SUM and AVG use dedicated aggregators rather than cudf::reduce's @@ -420,20 +433,21 @@ struct ReduceDecimalSumAggregator : ReduceAggregator { std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, + vector_size_t /* inputRowCount */, rmm::cuda_stream_view stream, - vector_size_t /*inputRowCount*/) override { + rmm::device_async_resource_ref mr) override { cudf::column_view inputCol = input.column(inputIndex); switch (step) { case core::AggregationNode::Step::kSingle: - return singleOrRawDecimalSumWithCast(inputCol, outputType, stream); + return singleOrRawDecimalSumWithCast(inputCol, outputType, stream, mr); case core::AggregationNode::Step::kPartial: - return partialDecimalSumCountToSerializedString(inputCol, stream); + return partialDecimalSumCountToSerializedString(inputCol, stream, mr); case core::AggregationNode::Step::kIntermediate: return reduceIntermediateDecimalFromSerializedColumn( - inputCol, outputType, stream); + inputCol, outputType, stream, mr); case core::AggregationNode::Step::kFinal: return reduceFinalDecimalSumFromSerializedColumn( - inputCol, outputType, stream); + inputCol, outputType, stream, mr); default: VELOX_NYI("Unsupported aggregation step for decimal sum reduce"); } @@ -451,21 +465,22 @@ struct ReduceDecimalAvgAggregator : ReduceAggregator { std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, + vector_size_t /* inputRowCount */, rmm::cuda_stream_view stream, - vector_size_t /*inputRowCount*/) override { + rmm::device_async_resource_ref mr) override { cudf::column_view inputCol = input.column(inputIndex); switch (step) { case core::AggregationNode::Step::kSingle: - return singleDecimalAvgFromRawColumn(inputCol, resultType, stream); + return singleDecimalAvgFromRawColumn(inputCol, resultType, stream, mr); case core::AggregationNode::Step::kPartial: - return partialDecimalSumCountToSerializedString(inputCol, stream); + return partialDecimalSumCountToSerializedString(inputCol, stream, mr); case core::AggregationNode::Step::kIntermediate: return reduceIntermediateDecimalFromSerializedColumn( - inputCol, outputType, stream); + inputCol, outputType, stream, mr); case core::AggregationNode::Step::kFinal: VELOX_CHECK(outputType == resultType, "outputType/resultType mismatch"); return reduceFinalDecimalAvgFromSerializedColumn( - inputCol, outputType, stream); + inputCol, outputType, stream, mr); default: VELOX_NYI("Unsupported aggregation step for decimal avg reduce"); } @@ -493,25 +508,27 @@ struct ApproxDistinctAggregator : ReduceAggregator { std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, + vector_size_t /* inputRowCount */, rmm::cuda_stream_view stream, - vector_size_t /*inputRowCount*/) override { + rmm::device_async_resource_ref mr) override { if (exec::isRawInput(step)) { - return doPartialReduce(input, stream); + return doPartialReduce(input, stream, mr); } else if (step == core::AggregationNode::Step::kIntermediate) { - return doIntermediateReduce(input, stream); + return doIntermediateReduce(input, stream, mr); } else { - return doFinalReduce(input, stream); + return doFinalReduce(input, stream, mr); } } private: std::unique_ptr makeSketchColumn( cuda::std::span sketch_bytes, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { auto sketch_size = static_cast(sketch_bytes.size()); cudf::size_type offsets[2] = {0, sketch_size}; - rmm::device_buffer offsets_device{2 * sizeof(cudf::size_type), stream}; + rmm::device_buffer offsets_device{2 * sizeof(cudf::size_type), stream, mr}; CUDF_CUDA_TRY(cudaMemcpyAsync( offsets_device.data(), offsets, @@ -519,7 +536,7 @@ struct ApproxDistinctAggregator : ReduceAggregator { cudaMemcpyHostToDevice, stream.value())); - rmm::device_buffer chars_buffer{sketch_bytes.size(), stream}; + rmm::device_buffer chars_buffer{sketch_bytes.size(), stream, mr}; CUDF_CUDA_TRY(cudaMemcpyAsync( chars_buffer.data(), sketch_bytes.data(), @@ -611,35 +628,38 @@ struct ApproxDistinctAggregator : ReduceAggregator { std::unique_ptr doPartialReduce( cudf::table_view const& input, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { auto inputTable = cudf::table_view({input.column(inputIndex)}); cudf::approx_distinct_count sketch{ inputTable, precision_, kNullPolicy, kNanPolicy, stream}; - return makeSketchColumn(sketch.sketch(), stream); + return makeSketchColumn(sketch.sketch(), stream, mr); } std::unique_ptr doIntermediateReduce( cudf::table_view const& input, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { auto sketch_column = input.column(inputIndex); if (sketch_column.size() == 0) { - return makeSketchColumn({}, stream); + return makeSketchColumn({}, stream, mr); } return mergeSketchesAndApply( sketch_column, - [this, stream](cudf::approx_distinct_count& sketch) { - return makeSketchColumn(sketch.sketch(), stream); + [this, stream, mr](cudf::approx_distinct_count& sketch) { + return makeSketchColumn(sketch.sketch(), stream, mr); }, stream); } std::unique_ptr doFinalReduce( cudf::table_view const& input, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { auto sketch_column = input.column(inputIndex); if (sketch_column.size() == 0) { @@ -647,19 +667,19 @@ struct ApproxDistinctAggregator : ReduceAggregator { cudf::numeric_scalar(0, true, stream, get_temp_mr()), 1, stream, - get_output_mr()); + mr); } return mergeSketchesAndApply( sketch_column, - [stream](cudf::approx_distinct_count& sketch) { + [stream, mr](cudf::approx_distinct_count& sketch) { std::size_t estimate = sketch.estimate(stream); return cudf::make_column_from_scalar( cudf::numeric_scalar( static_cast(estimate), true, stream, get_temp_mr()), 1, stream, - get_output_mr()); + mr); }, stream); } @@ -834,13 +854,14 @@ void CudfReduce::doAddInput(RowVectorPtr input) { CudfVectorPtr CudfReduce::doGlobalAggregation( cudf::table_view tableView, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { std::vector> resultColumns; resultColumns.reserve(aggregators_.size()); for (auto i = 0; i < aggregators_.size(); i++) { resultColumns.push_back( aggregators_[i]->doReduce( - tableView, outputType_->childAt(i), stream, numInputRows_)); + tableView, outputType_->childAt(i), numInputRows_, stream, mr)); } return std::make_shared( @@ -885,7 +906,7 @@ RowVectorPtr CudfReduce::doGetOutput() { ? tbl->view() : tbl->view().select( aggregationInputChannels_.begin(), aggregationInputChannels_.end()); - auto output = doGlobalAggregation(tableView, stream); + auto output = doGlobalAggregation(tableView, stream, get_output_mr()); if (isPartialOutput_ && !noMoreInput_) { numInputRows_ = 0; } diff --git a/velox/experimental/cudf/exec/CudfReduce.h b/velox/experimental/cudf/exec/CudfReduce.h index 117adc42f71..d983985afb6 100644 --- a/velox/experimental/cudf/exec/CudfReduce.h +++ b/velox/experimental/cudf/exec/CudfReduce.h @@ -29,8 +29,9 @@ struct ReduceAggregator { virtual std::unique_ptr doReduce( cudf::table_view const& input, TypePtr const& outputType, + vector_size_t inputRowCount, rmm::cuda_stream_view stream, - vector_size_t inputRowCount) = 0; + rmm::device_async_resource_ref mr) = 0; virtual ~ReduceAggregator() = default; @@ -91,7 +92,8 @@ class CudfReduce : public CudfOperatorBase { private: CudfVectorPtr doGlobalAggregation( cudf::table_view tableView, - rmm::cuda_stream_view stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); std::shared_ptr aggregationNode_; std::vector> aggregators_; diff --git a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp index 331c98c42ca..0e4d0794830 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp @@ -53,10 +53,11 @@ cudf::column_view castDecimal64InputToDecimal128( std::unique_ptr castCountColumnToInt64( std::unique_ptr count, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { if (count->type().id() != cudf::type_id::INT64) { count = cudf::cast( - *count, cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); + *count, cudf::data_type{cudf::type_id::INT64}, stream, mr); } return count; } @@ -64,21 +65,23 @@ std::unique_ptr castCountColumnToInt64( std::unique_ptr serializeDecimalPartialOrIntermediateState( std::unique_ptr sum, std::unique_ptr count, - rmm::cuda_stream_view stream) { - count = castCountColumnToInt64(std::move(count), stream); - return serializeDecimalSumState(sum->view(), count->view(), stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + count = castCountColumnToInt64(std::move(count), stream, mr); + return serializeDecimalSumState(sum->view(), count->view(), stream, mr); } std::unique_ptr finalizeDecimalAverage( std::unique_ptr sum, std::unique_ptr count, const TypePtr& resultType, - rmm::cuda_stream_view stream) { - count = castCountColumnToInt64(std::move(count), stream); - auto avgCol = computeDecimalAverage(sum->view(), count->view(), stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { + count = castCountColumnToInt64(std::move(count), stream, mr); + auto avgCol = computeDecimalAverage(sum->view(), count->view(), stream, mr); auto const cudfOutType = veloxToCudfDataType(resultType); if (avgCol->type() != cudfOutType) { - avgCol = cudf::cast(avgCol->view(), cudfOutType, stream, get_output_mr()); + avgCol = cudf::cast(avgCol->view(), cudfOutType, stream, mr); } return avgCol; } diff --git a/velox/experimental/cudf/exec/DecimalAggregationHostOps.h b/velox/experimental/cudf/exec/DecimalAggregationHostOps.h index 650b9ab6a32..f3be2cfe876 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationHostOps.h +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.h @@ -47,7 +47,8 @@ cudf::column_view castDecimal64InputToDecimal128( // output) when the incoming type differs. std::unique_ptr castCountColumnToInt64( std::unique_ptr count, - rmm::cuda_stream_view stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); // Normalizes the count column to INT64, then encodes sum and count into a // single STRING column of fixed-width per-row payloads (delegates to @@ -56,7 +57,8 @@ std::unique_ptr castCountColumnToInt64( std::unique_ptr serializeDecimalPartialOrIntermediateState( std::unique_ptr sum, std::unique_ptr count, - rmm::cuda_stream_view stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); // Normalizes the count column to INT64, computes a per-row decimal average // from intermediate sum/count (delegates to computeDecimalAverage), then casts @@ -66,6 +68,7 @@ std::unique_ptr finalizeDecimalAverage( std::unique_ptr sum, std::unique_ptr count, const TypePtr& resultType, - rmm::cuda_stream_view stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); } // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index d430cfcf84e..9075c966584 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -133,7 +133,8 @@ DecimalSumStateColumns deserializeDecimalSumState( std::unique_ptr serializeDecimalSumState( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, "Decimal sum state requires INT64 count column (type is {})", @@ -176,13 +177,13 @@ std::unique_ptr serializeDecimalSumState( numRows + 1, cudf::mask_state::UNALLOCATED, stream, - get_output_mr()); + mr); auto offsetsView = offsetsCol->mutable_view(); rmm::device_buffer charsBuf( static_cast(numRows) * detail::kDecimalSumStateSize, stream, - get_output_mr()); + mr); detail::fillOffsetsForDecimalSumState( useLargeOffsets, @@ -215,7 +216,7 @@ std::unique_ptr serializeDecimalSumState( stream); auto [nullMask, nullCount] = - detail::buildStateValidityMask(sumCol, countCol, stream, get_output_mr()); + detail::buildStateValidityMask(sumCol, countCol, stream, mr); return cudf::make_strings_column( static_cast(numRows), std::move(offsetsCol), @@ -227,7 +228,8 @@ std::unique_ptr serializeDecimalSumState( std::unique_ptr computeDecimalAverage( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream) { + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr) { VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, "Decimal average requires INT64 count column (type is {})", @@ -250,7 +252,7 @@ std::unique_ptr computeDecimalAverage( numRows, cudf::mask_state::UNALLOCATED, stream, - get_output_mr()); + mr); if (numRows > 0) { auto const rowCount = static_cast(numRows); @@ -266,7 +268,7 @@ std::unique_ptr computeDecimalAverage( } auto [nullMask, nullCount] = - detail::buildStateValidityMask(sumCol, countCol, stream, get_output_mr()); + detail::buildStateValidityMask(sumCol, countCol, stream, mr); if (nullCount > 0) { out->set_null_mask(std::move(nullMask), nullCount); } diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.h b/velox/experimental/cudf/exec/DecimalAggregationState.h index 2a31c6fc8ee..e184470a34a 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.h +++ b/velox/experimental/cudf/exec/DecimalAggregationState.h @@ -49,7 +49,8 @@ DecimalSumStateColumns deserializeDecimalSumState( std::unique_ptr serializeDecimalSumState( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); // Finalizes AVG from intermediate SUM state: divides each sum by its count // on device with decimal-specific rounding (see averageRoundDecimalSum), @@ -59,6 +60,7 @@ std::unique_ptr serializeDecimalSumState( std::unique_ptr computeDecimalAverage( const cudf::column_view& sumCol, const cudf::column_view& countCol, - rmm::cuda_stream_view stream); + rmm::cuda_stream_view stream, + rmm::device_async_resource_ref mr); } // namespace facebook::velox::cudf_velox diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index ddb20dd832a..c3b793d1033 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -1076,6 +1076,7 @@ TEST_F(CudfDecimalTest, decimalSumGlobalIntermediateVarbinaryAllNulls) { TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, -200, 300}; std::vector counts = {1, 2, 0}; std::vector sumValid = {true, false, true}; @@ -1084,7 +1085,7 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 2, stream); auto stateMask = copyNullMask(stateCol->view(), stream); auto sumMask = copyNullMask(sumAndCount.sum->view(), stream); @@ -1102,6 +1103,7 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal64) { TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal128) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(123450), static_cast<__int128_t>(-25000), @@ -1114,7 +1116,7 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateDecimal128) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 3, stream); auto stateMask = copyNullMask(stateCol->view(), stream); auto sumMask = copyNullMask(sumAndCount.sum->view(), stream); @@ -1177,6 +1179,7 @@ TEST_F(CudfDecimalTest, decimalDeserializeSumStateAllNull) { TEST_F(CudfDecimalTest, decimalSerializeSumStateUsesInt64OffsetsWhenEnabled) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); ScopedEnvVar enableLargeStrings("LIBCUDF_LARGE_STRINGS_ENABLED", "1"); ScopedEnvVar threshold("LIBCUDF_LARGE_STRINGS_THRESHOLD", "1"); @@ -1186,7 +1189,7 @@ TEST_F(CudfDecimalTest, decimalSerializeSumStateUsesInt64OffsetsWhenEnabled) { auto sumCol = makeDecimalColumn(sums, 2, nullptr, stream); auto countCol = makeInt64Column(counts, nullptr, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); cudf::strings_column_view strings(stateCol->view()); EXPECT_EQ(strings.offsets().type().id(), cudf::type_id::INT64); @@ -1194,6 +1197,7 @@ TEST_F(CudfDecimalTest, decimalSerializeSumStateUsesInt64OffsetsWhenEnabled) { TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); ScopedEnvVar enableLargeStrings("LIBCUDF_LARGE_STRINGS_ENABLED", "1"); ScopedEnvVar threshold("LIBCUDF_LARGE_STRINGS_THRESHOLD", "1"); @@ -1205,7 +1209,7 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); cudf::strings_column_view strings(stateCol->view()); EXPECT_EQ(strings.offsets().type().id(), cudf::type_id::INT64); @@ -1235,6 +1239,7 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripUsesInt64Offsets) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, 105, 250, -125}; std::vector counts = {4, 2, 0, 2}; std::vector sumValid = {true, true, true, true}; @@ -1242,7 +1247,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto avgMask = copyNullMask(avgCol->view(), stream); auto outAvg = copyColumnData(avgCol->view(), stream); @@ -1266,6 +1271,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(123450), static_cast<__int128_t>(-25000), @@ -1277,7 +1283,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto avgMask = copyNullMask(avgCol->view(), stream); auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); @@ -1304,6 +1310,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64MostNegativeSum) { // sign must be handled in the unsigned domain. avg of one INT64_MIN is // itself. auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); constexpr int64_t kMin = std::numeric_limits::min(); std::vector sums = {kMin}; std::vector counts = {1}; @@ -1311,7 +1318,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64MostNegativeSum) { auto sumCol = makeDecimalColumn(sums, 0, &valid, stream); auto countCol = makeInt64Column(counts, &valid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto outAvg = copyColumnData(avgCol->view(), stream); EXPECT_EQ(outAvg[0], kMin); @@ -1320,6 +1327,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64MostNegativeSum) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128MostNegativeSum) { // Same regression at the __int128 boundary. avg of one -2^127 is itself. auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); const __int128_t kMin = static_cast<__int128_t>(static_cast(1) << 127); std::vector<__int128_t> sums = {kMin}; @@ -1328,7 +1336,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128MostNegativeSum) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 0, &valid, stream); auto countCol = makeInt64Column(counts, &valid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); EXPECT_EQ(outAvg[0], kMin); @@ -1336,6 +1344,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128MostNegativeSum) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, 200, -150}; std::vector counts = {4, 5, 3}; std::vector sumValid = {true, true, true}; @@ -1343,7 +1352,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1367,6 +1376,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(90000), static_cast<__int128_t>(-5000), @@ -1378,7 +1388,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1402,6 +1412,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {80, -40, 1000}; std::vector counts = {2, 4, 10}; @@ -1410,7 +1421,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { ASSERT_FALSE(sumCol->nullable()); ASSERT_FALSE(countCol->nullable()); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1434,6 +1445,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(600), static_cast<__int128_t>(-99), @@ -1445,7 +1457,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { ASSERT_FALSE(sumCol->nullable()); ASSERT_FALSE(countCol->nullable()); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream); + auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1469,6 +1481,7 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector sums = {100, -200, 300, 400}; std::vector counts = {1, 0, 2, 3}; std::vector sumValid = {true, true, false, true}; @@ -1477,7 +1490,7 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); auto stateMask = copyNullMask(stateCol->view(), stream); auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 2, stream); @@ -1503,6 +1516,7 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal64) { TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal128) { auto stream = cudf::get_default_stream(); + auto mr = cudf::get_current_device_resource_ref(); std::vector<__int128_t> sums = { static_cast<__int128_t>(123450), static_cast<__int128_t>(-25000), @@ -1515,7 +1529,7 @@ TEST_F(CudfDecimalTest, decimalSumStateRoundTripDecimal128) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); auto stateCol = - serializeDecimalSumState(sumCol->view(), countCol->view(), stream); + serializeDecimalSumState(sumCol->view(), countCol->view(), stream, mr); auto stateMask = copyNullMask(stateCol->view(), stream); auto sumAndCount = deserializeDecimalSumState(stateCol->view(), 3, stream); From c43850fe389383b5158be766902108a52d6c53e7 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Thu, 11 Jun 2026 16:57:01 -0700 Subject: [PATCH 53/64] Format --- velox/experimental/cudf/exec/CudfGroupby.cpp | 37 ++++++++----------- velox/experimental/cudf/exec/CudfReduce.cpp | 26 +++++-------- .../cudf/exec/DecimalAggregationHostOps.cpp | 4 +- .../cudf/exec/DecimalAggregationState.cpp | 10 +---- .../cudf/tests/DecimalAggregationTest.cpp | 24 ++++++++---- 5 files changed, 46 insertions(+), 55 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index b905178c037..224f1b04fde 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -397,8 +397,7 @@ struct GroupbyCountAggregator : GroupbyAggregator { auto col = std::move(results[outputIndex_].results[0]); if (inputKind_ == CountInputKind::kNullConstant) { auto zero = cudf::numeric_scalar(0, true, stream, get_temp_mr()); - col = cudf::make_column_from_scalar( - zero, col->size(), stream, mr); + col = cudf::make_column_from_scalar(zero, col->size(), stream, mr); } // cudf produces int32 for count but velox expects int64. const auto cudfOutputType = cudf_velox::veloxToCudfDataType(resultType); @@ -487,12 +486,11 @@ struct GroupbyMeanAggregator : GroupbyAggregator { auto const cudfCountType = cudf_velox::veloxToCudfDataType(outputType->childAt(1)); if (sum->type() != cudf::data_type(cudfSumType)) { - sum = cudf::cast( - *sum, cudf::data_type(cudfSumType), stream, mr); + sum = cudf::cast(*sum, cudf::data_type(cudfSumType), stream, mr); } if (count->type() != cudf::data_type(cudfCountType)) { - count = cudf::cast( - *count, cudf::data_type(cudfCountType), stream, mr); + count = + cudf::cast(*count, cudf::data_type(cudfCountType), stream, mr); } auto children = std::vector>(); @@ -525,12 +523,11 @@ struct GroupbyMeanAggregator : GroupbyAggregator { auto const cudfCountType = cudf_velox::veloxToCudfDataType(outputType->childAt(1)); if (sum->type() != cudf::data_type(cudfSumType)) { - sum = cudf::cast( - *sum, cudf::data_type(cudfSumType), stream, mr); + sum = cudf::cast(*sum, cudf::data_type(cudfSumType), stream, mr); } if (count->type() != cudf::data_type(cudfCountType)) { - count = cudf::cast( - *count, cudf::data_type(cudfCountType), stream, mr); + count = + cudf::cast(*count, cudf::data_type(cudfCountType), stream, mr); } auto children = std::vector>(); @@ -651,12 +648,12 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { // Types don't match - need to copy and cast (use output_mr since // these become part of the output) - auto count = std::make_unique( - mergedView.child(0), stream, mr); - auto mean = std::make_unique( - mergedView.child(1), stream, mr); - auto m2 = std::make_unique( - mergedView.child(2), stream, mr); + auto count = + std::make_unique(mergedView.child(0), stream, mr); + auto mean = + std::make_unique(mergedView.child(1), stream, mr); + auto m2 = + std::make_unique(mergedView.child(2), stream, mr); return makeM2StructColumn( std::move(count), std::move(mean), std::move(m2), stream, mr); } @@ -704,8 +701,7 @@ struct GroupbyStddevSampAggregator : GroupbyAggregator { // Apply mask: where count < 2, result is NULL cudf::numeric_scalar nullDouble( 0.0, false, stream, get_temp_mr()); - return cudf::copy_if_else( - *stddev, nullDouble, *validMask, stream, mr); + return cudf::copy_if_else(*stddev, nullDouble, *validMask, stream, mr); } default: VELOX_NYI("Unsupported aggregation step for stddev_samp"); @@ -987,7 +983,7 @@ void CudfGroupby::computePartialGroupbyStreaming(CudfVectorPtr tbl) { aggregators_, bufferedResultType_, inputTableStream, - get_output_mr()); + get_output_mr()); // If we already have partial output, concatenate the new results with it. if (bufferedResult_) { @@ -1144,8 +1140,7 @@ CudfVectorPtr CudfGroupby::doGroupByAggregation( aggregator->addGroupbyRequest(tableView, requests, stream); } - auto [groupKeys, results] = - groupByOwner.aggregate(requests, stream, mr); + auto [groupKeys, results] = groupByOwner.aggregate(requests, stream, mr); // flatten the results std::vector> resultColumns; diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 0c529ca89bb..38caf1cbaa9 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -76,8 +76,7 @@ using facebook::velox::cudf_velox::validateIntermediateColumnType; cudfOutputType, \ stream, \ get_temp_mr()); \ - return cudf::make_column_from_scalar( \ - *resultScalar, 1, stream, mr); \ + return cudf::make_column_from_scalar(*resultScalar, 1, stream, mr); \ } \ }; @@ -125,8 +124,7 @@ struct ReduceCountAggregator : ReduceAggregator { auto resultScalar = cudf::numeric_scalar(count, true, stream, get_temp_mr()); - return cudf::make_column_from_scalar( - resultScalar, 1, stream, mr); + return cudf::make_column_from_scalar(resultScalar, 1, stream, mr); } else { // For non-raw input (intermediate/final), use sum aggregation auto const aggRequest = @@ -139,8 +137,7 @@ struct ReduceCountAggregator : ReduceAggregator { stream, get_temp_mr()); resultScalar->set_valid_async(true, stream); - return cudf::make_column_from_scalar( - *resultScalar, 1, stream, mr); + return cudf::make_column_from_scalar(*resultScalar, 1, stream, mr); } } @@ -173,8 +170,7 @@ struct ReduceMeanAggregator : ReduceAggregator { cudfOutputType, stream, get_temp_mr()); - return cudf::make_column_from_scalar( - *resultScalar, 1, stream, mr); + return cudf::make_column_from_scalar(*resultScalar, 1, stream, mr); } case core::AggregationNode::Step::kPartial: { VELOX_CHECK(outputType->isRow()); @@ -193,8 +189,8 @@ struct ReduceMeanAggregator : ReduceAggregator { cudfSumType, stream, get_temp_mr()); - auto sumCol = cudf::make_column_from_scalar( - *sumResultScalar, 1, stream, mr); + auto sumCol = + cudf::make_column_from_scalar(*sumResultScalar, 1, stream, mr); // libcudf doesn't have a count agg for reduce. What we want is to // count the number of valid rows. @@ -231,8 +227,8 @@ struct ReduceMeanAggregator : ReduceAggregator { cudf::make_sum_aggregation(); auto const sumResultScalar = cudf::reduce( sumCol, *sumAggRequest, sumCol.type(), stream, get_temp_mr()); - auto sumResultCol = cudf::make_column_from_scalar( - *sumResultScalar, 1, stream, mr); + auto sumResultCol = + cudf::make_column_from_scalar(*sumResultScalar, 1, stream, mr); // sum the counts auto const countAggRequest = @@ -264,8 +260,7 @@ cudf_velox::DecimalSumStateColumns makeSumCountColumns( rmm::device_async_resource_ref mr) { cudf_velox::DecimalSumStateColumns cols; cols.sum = cudf::make_column_from_scalar(sumScalar, 1, stream, mr); - cols.count = - cudf::make_column_from_scalar(countScalar, 1, stream, mr); + cols.count = cudf::make_column_from_scalar(countScalar, 1, stream, mr); return cols; } @@ -377,8 +372,7 @@ std::unique_ptr singleOrRawDecimalSumWithCast( } auto const resultScalar = cudf::reduce(inputCol, *sumAgg, cudfOutType, stream, get_temp_mr()); - return cudf::make_column_from_scalar( - *resultScalar, 1, stream, mr); + return cudf::make_column_from_scalar(*resultScalar, 1, stream, mr); } std::unique_ptr reduceIntermediateDecimalFromSerializedColumn( diff --git a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp index 0e4d0794830..428b916e2cc 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp @@ -56,8 +56,8 @@ std::unique_ptr castCountColumnToInt64( rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { if (count->type().id() != cudf::type_id::INT64) { - count = cudf::cast( - *count, cudf::data_type{cudf::type_id::INT64}, stream, mr); + count = + cudf::cast(*count, cudf::data_type{cudf::type_id::INT64}, stream, mr); } return count; } diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index 9075c966584..a0756d82a7c 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -181,9 +181,7 @@ std::unique_ptr serializeDecimalSumState( auto offsetsView = offsetsCol->mutable_view(); rmm::device_buffer charsBuf( - static_cast(numRows) * detail::kDecimalSumStateSize, - stream, - mr); + static_cast(numRows) * detail::kDecimalSumStateSize, stream, mr); detail::fillOffsetsForDecimalSumState( useLargeOffsets, @@ -248,11 +246,7 @@ std::unique_ptr computeDecimalAverage( auto numRows = sumCol.size(); auto out = cudf::make_fixed_width_column( - sumCol.type(), - numRows, - cudf::mask_state::UNALLOCATED, - stream, - mr); + sumCol.type(), numRows, cudf::mask_state::UNALLOCATED, stream, mr); if (numRows > 0) { auto const rowCount = static_cast(numRows); diff --git a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp index c3b793d1033..e7d499621ca 100644 --- a/velox/experimental/cudf/tests/DecimalAggregationTest.cpp +++ b/velox/experimental/cudf/tests/DecimalAggregationTest.cpp @@ -1247,7 +1247,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto avgMask = copyNullMask(avgCol->view(), stream); auto outAvg = copyColumnData(avgCol->view(), stream); @@ -1283,7 +1284,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto avgMask = copyNullMask(avgCol->view(), stream); auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); @@ -1318,7 +1320,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64MostNegativeSum) { auto sumCol = makeDecimalColumn(sums, 0, &valid, stream); auto countCol = makeInt64Column(counts, &valid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto outAvg = copyColumnData(avgCol->view(), stream); EXPECT_EQ(outAvg[0], kMin); @@ -1336,7 +1339,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128MostNegativeSum) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 0, &valid, stream); auto countCol = makeInt64Column(counts, &valid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); auto outAvg = copyColumnData<__int128_t>(avgCol->view(), stream); EXPECT_EQ(outAvg[0], kMin); @@ -1352,7 +1356,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64AllValid) { auto sumCol = makeDecimalColumn(sums, 2, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1388,7 +1393,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128AllValid) { auto sumCol = makeDecimalColumn<__int128_t>(sums, 3, &sumValid, stream); auto countCol = makeInt64Column(counts, &countValid, stream); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1421,7 +1427,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal64NonNullableInputs) { ASSERT_FALSE(sumCol->nullable()); ASSERT_FALSE(countCol->nullable()); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); @@ -1457,7 +1464,8 @@ TEST_F(CudfDecimalTest, decimalComputeAverageDecimal128NonNullableInputs) { ASSERT_FALSE(sumCol->nullable()); ASSERT_FALSE(countCol->nullable()); - auto avgCol = computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); + auto avgCol = + computeDecimalAverage(sumCol->view(), countCol->view(), stream, mr); EXPECT_EQ(avgCol->view().null_count(), 0); auto avgMask = copyNullMask(avgCol->view(), stream); From 01685c0e783adfc97a66da1887d969766abdf085 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Wed, 10 Jun 2026 14:02:56 -0700 Subject: [PATCH 54/64] Refactor to use cudf::type_dispatcher --- .../cudf/exec/DecimalAggregationDevice.cu | 221 +++++++++--------- .../cudf/exec/DecimalAggregationDevice.h | 146 ++++++++---- .../cudf/exec/DecimalAggregationState.cpp | 69 +++--- 3 files changed, 251 insertions(+), 185 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index a24c2b71171..e62765a3cf1 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -234,133 +234,146 @@ std::pair buildStateValidityMaskImpl( namespace detail { -void fillOffsetsForDecimalSumState( - bool use64BitOffsets, - void* offsetsMutable, - int32_t numRows, - rmm::cuda_stream_view stream) { - // The offsets buffer holds numRows + 1 entries. - auto const n = static_cast(numRows) + 1; - if (use64BitOffsets) { - launchFillOffsets( - cuda::std::span{static_cast(offsetsMutable), n}, - stream); - } else { - launchFillOffsets( - cuda::std::span{static_cast(offsetsMutable), n}, - stream); - } +template <> +void fillOffsetsForDecimalSumState::operator()( + cudf::mutable_column_view offsetsView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { + launchFillOffsets( + cuda::std::span{ + offsetsView.data(), static_cast(numRows) + 1}, + stream); } -void packDecimalSumState( - cudf::type_id sumType, - bool use64BitOffsets, - const void* sumPtr, - const int64_t* countPtr, - const void* offsetsPtr, - uint8_t* chars, - int32_t numRows, - rmm::cuda_stream_view stream) { +template <> +void fillOffsetsForDecimalSumState::operator()( + cudf::mutable_column_view offsetsView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { + launchFillOffsets( + cuda::std::span{ + offsetsView.data(), static_cast(numRows) + 1}, + stream); +} + +template <> +void unpackDecimalSumState::operator()( + cudf::column_view offsetsView, + const uint8_t* chars, + cudf::mutable_column_view sumView, + cudf::mutable_column_view countView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { auto const n = static_cast(numRows); - cuda::std::span counts{countPtr, n}; - if (use64BitOffsets) { - cuda::std::span offsets{ - static_cast(offsetsPtr), n}; - if (sumType == cudf::type_id::DECIMAL64) { - launchPackState( - cuda::std::span{ - static_cast(sumPtr), n}, - counts, - offsets, - chars, - stream); - } else { - launchPackState( - cuda::std::span{ - static_cast(sumPtr), n}, - counts, - offsets, - chars, - stream); - } - } else { - cuda::std::span offsets{ - static_cast(offsetsPtr), n}; - if (sumType == cudf::type_id::DECIMAL64) { - launchPackState( - cuda::std::span{ - static_cast(sumPtr), n}, - counts, - offsets, - chars, - stream); - } else { - launchPackState( - cuda::std::span{ - static_cast(sumPtr), n}, - counts, - offsets, - chars, - stream); - } - } + launchUnpackState( + cuda::std::span{offsetsView.data(), n}, + chars, + cuda::std::span<__int128_t>{sumView.data<__int128_t>(), n}, + cuda::std::span{countView.data(), n}, + stream); } -void unpackDecimalSumState( - bool offsets64, - const void* offsetsPtr, +template <> +void unpackDecimalSumState::operator()( + cudf::column_view offsetsView, const uint8_t* chars, - __int128_t* sums, - int64_t* counts, - int32_t numRows, - rmm::cuda_stream_view stream) { + cudf::mutable_column_view sumView, + cudf::mutable_column_view countView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { + auto const n = static_cast(numRows); + launchUnpackState( + cuda::std::span{offsetsView.data(), n}, + chars, + cuda::std::span<__int128_t>{sumView.data<__int128_t>(), n}, + cuda::std::span{countView.data(), n}, + stream); +} + +template <> +void packDecimalSumState::operator()( + cudf::column_view sumCol, + const int64_t* counts, + cudf::column_view offsetsView, + uint8_t* chars, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { auto const n = static_cast(numRows); - cuda::std::span<__int128_t> sumsSpan{sums, n}; - cuda::std::span countsSpan{counts, n}; - if (offsets64) { - launchUnpackState( - cuda::std::span{ - static_cast(offsetsPtr), n}, + auto const sums = sumCol.data(); + if (offsetsView.type().id() == cudf::type_id::INT32) { + launchPackState( + cuda::std::span{sums, n}, + cuda::std::span{counts, n}, + cuda::std::span{offsetsView.data(), n}, chars, - sumsSpan, - countsSpan, stream); } else { - launchUnpackState( - cuda::std::span{ - static_cast(offsetsPtr), n}, + launchPackState( + cuda::std::span{sums, n}, + cuda::std::span{counts, n}, + cuda::std::span{offsetsView.data(), n}, chars, - sumsSpan, - countsSpan, stream); } } -void averageRoundDecimalSum( - cudf::type_id sumType, - const void* sums, +template <> +void packDecimalSumState::operator()<__int128_t, 0>( + cudf::column_view sumCol, const int64_t* counts, - void* out, - int32_t numRows, - rmm::cuda_stream_view stream) { + cudf::column_view offsetsView, + uint8_t* chars, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { auto const n = static_cast(numRows); - cuda::std::span countsSpan{counts, n}; - if (sumType == cudf::type_id::DECIMAL64) { - launchAvgRound( - cuda::std::span{static_cast(sums), n}, - countsSpan, - cuda::std::span{static_cast(out), n}, + auto const sums = sumCol.data<__int128_t>(); + if (offsetsView.type().id() == cudf::type_id::INT32) { + launchPackState( + cuda::std::span{sums, n}, + cuda::std::span{counts, n}, + cuda::std::span{offsetsView.data(), n}, + chars, stream); } else { - launchAvgRound( - cuda::std::span{ - static_cast(sums), n}, - countsSpan, - cuda::std::span<__int128_t>{static_cast<__int128_t*>(out), n}, + launchPackState( + cuda::std::span{sums, n}, + cuda::std::span{counts, n}, + cuda::std::span{offsetsView.data(), n}, + chars, stream); } } +template <> +void averageRoundDecimalSum::operator()( + cudf::column_view sumCol, + const int64_t* counts, + cudf::mutable_column_view outView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { + auto const n = static_cast(numRows); + launchAvgRound( + cuda::std::span{sumCol.data(), n}, + cuda::std::span{counts, n}, + cuda::std::span{outView.data(), n}, + stream); +} + +template <> +void averageRoundDecimalSum::operator()<__int128_t, 0>( + cudf::column_view sumCol, + const int64_t* counts, + cudf::mutable_column_view outView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const { + auto const n = static_cast(numRows); + launchAvgRound( + cuda::std::span{sumCol.data<__int128_t>(), n}, + cuda::std::span{counts, n}, + cuda::std::span<__int128_t>{outView.data<__int128_t>(), n}, + stream); +} + std::pair buildStateValidityMask( const cudf::column_view& sumCol, const cudf::column_view& countCol, diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.h b/velox/experimental/cudf/exec/DecimalAggregationDevice.h index 25411ca1a2b..e69c0cff3e0 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.h +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.h @@ -22,11 +22,21 @@ #include #include +#include #include +#include #include namespace facebook::velox::cudf_velox::detail { +template +inline constexpr bool isDecimalSumStorageType = + std::is_same_v || std::is_same_v; + +template +inline constexpr bool isOffsetStorageType = + std::is_same_v || std::is_same_v; + // Size in bytes of each row's packed decimal SUM intermediate state in the // strings payload (count, overflow placeholder, and 128-bit sum split into // words). @@ -35,78 +45,128 @@ constexpr size_t kDecimalSumStateSize = 32; /** * Writes strings-style prefix offsets: offset[i] == i * kDecimalSumStateSize. * - * @param use64BitOffsets whether offsets are INT64 (else INT32). - * @param offsetsMutable output buffer of numRows + 1 offset elements. + * @param offsetsView output offsets column of numRows + 1 elements. * @param numRows number of payload rows. * @param stream CUDA stream for the launch. */ -void fillOffsetsForDecimalSumState( - bool use64BitOffsets, - void* offsetsMutable, - cudf::size_type numRows, - rmm::cuda_stream_view stream); +struct fillOffsetsForDecimalSumState { + template < + typename OffsetT, + std::enable_if_t, int> = 0> + void operator()( + cudf::mutable_column_view offsetsView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; + + template < + typename OffsetT, + std::enable_if_t, int> = 0> + void operator()( + cudf::mutable_column_view offsetsView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const {} +}; /** * Encodes each row's partial sum and count into the fixed-width device layout * used for VARBINARY interchange. * - * @param sumType element type of sumPtr (DECIMAL64 or DECIMAL128). - * @param use64BitOffsets whether offsetsPtr is INT64 (else INT32). - * @param sumPtr per-row sums. - * @param countPtr per-row int64 counts. - * @param offsetsPtr per-row byte offsets into chars. + * @param sumCol per-row sums. + * @param counts per-row int64 counts. + * @param offsetsView per-row byte offsets into chars. * @param chars output payload buffer. * @param numRows number of rows. * @param stream CUDA stream for the launch. */ -void packDecimalSumState( - cudf::type_id sumType, - bool use64BitOffsets, - const void* sumPtr, - const int64_t* countPtr, - const void* offsetsPtr, - uint8_t* chars, - cudf::size_type numRows, - rmm::cuda_stream_view stream); +struct packDecimalSumState { + template < + typename SumT, + std::enable_if_t, int> = 0> + void operator()( + cudf::column_view sumCol, + const int64_t* counts, + cudf::column_view offsetsView, + uint8_t* chars, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; + + template < + typename SumT, + std::enable_if_t, int> = 0> + void operator()( + cudf::column_view sumCol, + const int64_t* counts, + cudf::column_view offsetsView, + uint8_t* chars, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const {} +}; /** * Inverse of packDecimalSumState. * - * @param offsets64 whether offsetsPtr is INT64 (else INT32). - * @param offsetsPtr per-row byte offsets into chars. + * @param offsetsView per-row byte offsets into chars. * @param chars packed payload buffer. - * @param sums output per-row DECIMAL128 sums. - * @param counts output per-row counts. + * @param sumView output per-row DECIMAL128 sums. + * @param countView output per-row counts. * @param numRows number of rows. * @param stream CUDA stream for the launch. */ -void unpackDecimalSumState( - bool offsets64, - const void* offsetsPtr, - const uint8_t* chars, - __int128_t* sums, - int64_t* counts, - cudf::size_type numRows, - rmm::cuda_stream_view stream); +struct unpackDecimalSumState { + template < + typename OffsetT, + std::enable_if_t, int> = 0> + void operator()( + cudf::column_view offsetsView, + const uint8_t* chars, + cudf::mutable_column_view sumView, + cudf::mutable_column_view countView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; + + template < + typename OffsetT, + std::enable_if_t, int> = 0> + void operator()( + cudf::column_view offsetsView, + const uint8_t* chars, + cudf::mutable_column_view sumView, + cudf::mutable_column_view countView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const {} +}; /** * Per-row half-up integer divide of sum by count; count == 0 writes zero * (validity is applied separately). * - * @param sumType element type of sums/out (DECIMAL64 or DECIMAL128). - * @param sums per-row sums. + * @param sumCol per-row sums. * @param counts per-row counts. - * @param out output per-row averages. + * @param outView output per-row averages. * @param numRows number of rows. * @param stream CUDA stream for the launch. */ -void averageRoundDecimalSum( - cudf::type_id sumType, - const void* sums, - const int64_t* counts, - void* out, - cudf::size_type numRows, - rmm::cuda_stream_view stream); +struct averageRoundDecimalSum { + template < + typename SumT, + std::enable_if_t, int> = 0> + void operator()( + cudf::column_view sumCol, + const int64_t* counts, + cudf::mutable_column_view outView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; + + template < + typename SumT, + std::enable_if_t, int> = 0> + void operator()( + cudf::column_view sumCol, + const int64_t* counts, + cudf::mutable_column_view outView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const {} +}; /** * Builds a null mask for rows where sum and count are both valid and count is diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index a0756d82a7c..6238bb3a885 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -81,7 +82,6 @@ DecimalSumStateColumns deserializeDecimalSumState( cudf::strings_column_view strings(stateCol); auto offsetsView = strings.offsets(); - auto offsetsType = offsetsView.type().id(); auto charsPtr = reinterpret_cast(strings.chars_begin(stream)); auto sumCol = cudf::make_fixed_width_column( @@ -101,18 +101,19 @@ DecimalSumStateColumns deserializeDecimalSumState( auto countView = countCol->mutable_view(); // numRows is guaranteed positive here - const bool offsets64 = (offsetsType == cudf::type_id::INT64); + auto const offsetsType = offsetsView.type().id(); VELOX_CHECK( - offsets64 || offsetsType == cudf::type_id::INT32, + offsetsType == cudf::type_id::INT32 || + offsetsType == cudf::type_id::INT64, "Decimal sum state requires INT32 or INT64 offsets (offset type is {})", - static_cast(offsetsType)); - detail::unpackDecimalSumState( - offsets64, - offsets64 ? static_cast(offsetsView.data()) - : static_cast(offsetsView.data()), + cudf::type_to_name(offsetsView.type())); + cudf::type_dispatcher( + offsetsView.type(), + detail::unpackDecimalSumState{}, + offsetsView, charsPtr, - sumView.data<__int128_t>(), - countView.data(), + sumView, + countView, numRows, stream); @@ -183,32 +184,25 @@ std::unique_ptr serializeDecimalSumState( rmm::device_buffer charsBuf( static_cast(numRows) * detail::kDecimalSumStateSize, stream, mr); - detail::fillOffsetsForDecimalSumState( - useLargeOffsets, - useLargeOffsets ? static_cast(offsetsView.data()) - : static_cast(offsetsView.data()), + auto charsPtr = reinterpret_cast(charsBuf.data()); + cudf::type_dispatcher( + offsetsView.type(), + detail::fillOffsetsForDecimalSumState{}, + offsetsView, rowCount, stream); - auto charsPtr = reinterpret_cast(charsBuf.data()); - const void* offsetsPtr = useLargeOffsets - ? static_cast(offsetsView.data()) - : static_cast(offsetsView.data()); - const auto sumType = sumCol.type().id(); VELOX_CHECK( - sumType == cudf::type_id::DECIMAL64 || - sumType == cudf::type_id::DECIMAL128, + sumCol.type().id() == cudf::type_id::DECIMAL64 || + sumCol.type().id() == cudf::type_id::DECIMAL128, "Unsupported decimal sum column type (type is {})", cudf::type_to_name(sumCol.type())); - const void* sumPtr = sumType == cudf::type_id::DECIMAL64 - ? static_cast(sumCol.data()) - : static_cast(sumCol.data<__int128_t>()); - detail::packDecimalSumState( - sumType, - useLargeOffsets, - sumPtr, + cudf::type_dispatcher( + sumCol.type(), + detail::packDecimalSumState{}, + sumCol, countCol.data(), - offsetsPtr, + offsetsView, charsPtr, rowCount, stream); @@ -250,15 +244,14 @@ std::unique_ptr computeDecimalAverage( if (numRows > 0) { auto const rowCount = static_cast(numRows); - const auto sumType = sumCol.type().id(); - const void* sumsPtr = sumType == cudf::type_id::DECIMAL64 - ? static_cast(sumCol.data()) - : static_cast(sumCol.data<__int128_t>()); - void* outPtr = sumType == cudf::type_id::DECIMAL64 - ? static_cast(out->mutable_view().data()) - : static_cast(out->mutable_view().data<__int128_t>()); - detail::averageRoundDecimalSum( - sumType, sumsPtr, countCol.data(), outPtr, rowCount, stream); + cudf::type_dispatcher( + sumCol.type(), + detail::averageRoundDecimalSum{}, + sumCol, + countCol.data(), + out->mutable_view(), + rowCount, + stream); } auto [nullMask, nullCount] = From 3a0285f3e8f803d1e04db7dfb27375b9a09a6901 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 12 Jun 2026 10:04:51 -0700 Subject: [PATCH 55/64] Combine pairs of functions, per @bdice --- .../cudf/exec/DecimalAggregationDevice.cu | 114 ++++++++---------- 1 file changed, 48 insertions(+), 66 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index e62765a3cf1..37daa5ec2e0 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -234,30 +234,28 @@ std::pair buildStateValidityMaskImpl( namespace detail { -template <> -void fillOffsetsForDecimalSumState::operator()( +template , int>> +void fillOffsetsForDecimalSumState::operator()( cudf::mutable_column_view offsetsView, cudf::size_type numRows, rmm::cuda_stream_view stream) const { launchFillOffsets( - cuda::std::span{ - offsetsView.data(), static_cast(numRows) + 1}, + cuda::std::span{ + offsetsView.data(), static_cast(numRows) + 1}, stream); } -template <> -void fillOffsetsForDecimalSumState::operator()( +template void fillOffsetsForDecimalSumState::operator()( cudf::mutable_column_view offsetsView, cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - launchFillOffsets( - cuda::std::span{ - offsetsView.data(), static_cast(numRows) + 1}, - stream); -} + rmm::cuda_stream_view stream) const; +template void fillOffsetsForDecimalSumState::operator()( + cudf::mutable_column_view offsetsView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; -template <> -void unpackDecimalSumState::operator()( +template , int>> +void unpackDecimalSumState::operator()( cudf::column_view offsetsView, const uint8_t* chars, cudf::mutable_column_view sumView, @@ -266,32 +264,30 @@ void unpackDecimalSumState::operator()( rmm::cuda_stream_view stream) const { auto const n = static_cast(numRows); launchUnpackState( - cuda::std::span{offsetsView.data(), n}, + cuda::std::span{offsetsView.data(), n}, chars, cuda::std::span<__int128_t>{sumView.data<__int128_t>(), n}, cuda::std::span{countView.data(), n}, stream); } -template <> -void unpackDecimalSumState::operator()( +template void unpackDecimalSumState::operator()( cudf::column_view offsetsView, const uint8_t* chars, cudf::mutable_column_view sumView, cudf::mutable_column_view countView, cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - auto const n = static_cast(numRows); - launchUnpackState( - cuda::std::span{offsetsView.data(), n}, - chars, - cuda::std::span<__int128_t>{sumView.data<__int128_t>(), n}, - cuda::std::span{countView.data(), n}, - stream); -} + rmm::cuda_stream_view stream) const; +template void unpackDecimalSumState::operator()( + cudf::column_view offsetsView, + const uint8_t* chars, + cudf::mutable_column_view sumView, + cudf::mutable_column_view countView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; -template <> -void packDecimalSumState::operator()( +template , int>> +void packDecimalSumState::operator()( cudf::column_view sumCol, const int64_t* counts, cudf::column_view offsetsView, @@ -299,17 +295,17 @@ void packDecimalSumState::operator()( cudf::size_type numRows, rmm::cuda_stream_view stream) const { auto const n = static_cast(numRows); - auto const sums = sumCol.data(); + auto const sums = sumCol.data(); if (offsetsView.type().id() == cudf::type_id::INT32) { launchPackState( - cuda::std::span{sums, n}, + cuda::std::span{sums, n}, cuda::std::span{counts, n}, cuda::std::span{offsetsView.data(), n}, chars, stream); } else { launchPackState( - cuda::std::span{sums, n}, + cuda::std::span{sums, n}, cuda::std::span{counts, n}, cuda::std::span{offsetsView.data(), n}, chars, @@ -317,35 +313,23 @@ void packDecimalSumState::operator()( } } -template <> -void packDecimalSumState::operator()<__int128_t, 0>( +template void packDecimalSumState::operator()( cudf::column_view sumCol, const int64_t* counts, cudf::column_view offsetsView, uint8_t* chars, cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - auto const n = static_cast(numRows); - auto const sums = sumCol.data<__int128_t>(); - if (offsetsView.type().id() == cudf::type_id::INT32) { - launchPackState( - cuda::std::span{sums, n}, - cuda::std::span{counts, n}, - cuda::std::span{offsetsView.data(), n}, - chars, - stream); - } else { - launchPackState( - cuda::std::span{sums, n}, - cuda::std::span{counts, n}, - cuda::std::span{offsetsView.data(), n}, - chars, - stream); - } -} + rmm::cuda_stream_view stream) const; +template void packDecimalSumState::operator()<__int128_t, 0>( + cudf::column_view sumCol, + const int64_t* counts, + cudf::column_view offsetsView, + uint8_t* chars, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; -template <> -void averageRoundDecimalSum::operator()( +template , int>> +void averageRoundDecimalSum::operator()( cudf::column_view sumCol, const int64_t* counts, cudf::mutable_column_view outView, @@ -353,26 +337,24 @@ void averageRoundDecimalSum::operator()( rmm::cuda_stream_view stream) const { auto const n = static_cast(numRows); launchAvgRound( - cuda::std::span{sumCol.data(), n}, + cuda::std::span{sumCol.data(), n}, cuda::std::span{counts, n}, - cuda::std::span{outView.data(), n}, + cuda::std::span{outView.data(), n}, stream); } -template <> -void averageRoundDecimalSum::operator()<__int128_t, 0>( +template void averageRoundDecimalSum::operator()( cudf::column_view sumCol, const int64_t* counts, cudf::mutable_column_view outView, cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - auto const n = static_cast(numRows); - launchAvgRound( - cuda::std::span{sumCol.data<__int128_t>(), n}, - cuda::std::span{counts, n}, - cuda::std::span<__int128_t>{outView.data<__int128_t>(), n}, - stream); -} + rmm::cuda_stream_view stream) const; +template void averageRoundDecimalSum::operator()<__int128_t, 0>( + cudf::column_view sumCol, + const int64_t* counts, + cudf::mutable_column_view outView, + cudf::size_type numRows, + rmm::cuda_stream_view stream) const; std::pair buildStateValidityMask( const cudf::column_view& sumCol, From f931ad6c16be0b96867a1c31cb46c342c7118727 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 12 Jun 2026 10:12:26 -0700 Subject: [PATCH 56/64] Use C++20 requires/concepts instead of enable_if/traits, per @bdice --- .../cudf/exec/DecimalAggregationDevice.cu | 28 ++++++----- .../cudf/exec/DecimalAggregationDevice.h | 50 ++++++++----------- 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index 37daa5ec2e0..ea56801d86e 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -234,7 +234,8 @@ std::pair buildStateValidityMaskImpl( namespace detail { -template , int>> +template + requires OffsetStorageType void fillOffsetsForDecimalSumState::operator()( cudf::mutable_column_view offsetsView, cudf::size_type numRows, @@ -245,16 +246,17 @@ void fillOffsetsForDecimalSumState::operator()( stream); } -template void fillOffsetsForDecimalSumState::operator()( +template void fillOffsetsForDecimalSumState::operator()( cudf::mutable_column_view offsetsView, cudf::size_type numRows, rmm::cuda_stream_view stream) const; -template void fillOffsetsForDecimalSumState::operator()( +template void fillOffsetsForDecimalSumState::operator()( cudf::mutable_column_view offsetsView, cudf::size_type numRows, rmm::cuda_stream_view stream) const; -template , int>> +template + requires OffsetStorageType void unpackDecimalSumState::operator()( cudf::column_view offsetsView, const uint8_t* chars, @@ -271,14 +273,14 @@ void unpackDecimalSumState::operator()( stream); } -template void unpackDecimalSumState::operator()( +template void unpackDecimalSumState::operator()( cudf::column_view offsetsView, const uint8_t* chars, cudf::mutable_column_view sumView, cudf::mutable_column_view countView, cudf::size_type numRows, rmm::cuda_stream_view stream) const; -template void unpackDecimalSumState::operator()( +template void unpackDecimalSumState::operator()( cudf::column_view offsetsView, const uint8_t* chars, cudf::mutable_column_view sumView, @@ -286,7 +288,8 @@ template void unpackDecimalSumState::operator()( cudf::size_type numRows, rmm::cuda_stream_view stream) const; -template , int>> +template + requires DecimalSumStorageType void packDecimalSumState::operator()( cudf::column_view sumCol, const int64_t* counts, @@ -313,14 +316,14 @@ void packDecimalSumState::operator()( } } -template void packDecimalSumState::operator()( +template void packDecimalSumState::operator()( cudf::column_view sumCol, const int64_t* counts, cudf::column_view offsetsView, uint8_t* chars, cudf::size_type numRows, rmm::cuda_stream_view stream) const; -template void packDecimalSumState::operator()<__int128_t, 0>( +template void packDecimalSumState::operator()<__int128_t>( cudf::column_view sumCol, const int64_t* counts, cudf::column_view offsetsView, @@ -328,7 +331,8 @@ template void packDecimalSumState::operator()<__int128_t, 0>( cudf::size_type numRows, rmm::cuda_stream_view stream) const; -template , int>> +template + requires DecimalSumStorageType void averageRoundDecimalSum::operator()( cudf::column_view sumCol, const int64_t* counts, @@ -343,13 +347,13 @@ void averageRoundDecimalSum::operator()( stream); } -template void averageRoundDecimalSum::operator()( +template void averageRoundDecimalSum::operator()( cudf::column_view sumCol, const int64_t* counts, cudf::mutable_column_view outView, cudf::size_type numRows, rmm::cuda_stream_view stream) const; -template void averageRoundDecimalSum::operator()<__int128_t, 0>( +template void averageRoundDecimalSum::operator()<__int128_t>( cudf::column_view sumCol, const int64_t* counts, cudf::mutable_column_view outView, diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.h b/velox/experimental/cudf/exec/DecimalAggregationDevice.h index e69c0cff3e0..152b3652501 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.h +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.h @@ -22,20 +22,20 @@ #include #include +#include #include #include -#include #include namespace facebook::velox::cudf_velox::detail { template -inline constexpr bool isDecimalSumStorageType = - std::is_same_v || std::is_same_v; +concept OffsetStorageType = + std::same_as || std::same_as; template -inline constexpr bool isOffsetStorageType = - std::is_same_v || std::is_same_v; +concept DecimalSumStorageType = + std::same_as || std::same_as; // Size in bytes of each row's packed decimal SUM intermediate state in the // strings payload (count, overflow placeholder, and 128-bit sum split into @@ -50,17 +50,15 @@ constexpr size_t kDecimalSumStateSize = 32; * @param stream CUDA stream for the launch. */ struct fillOffsetsForDecimalSumState { - template < - typename OffsetT, - std::enable_if_t, int> = 0> + template + requires OffsetStorageType void operator()( cudf::mutable_column_view offsetsView, cudf::size_type numRows, rmm::cuda_stream_view stream) const; - template < - typename OffsetT, - std::enable_if_t, int> = 0> + template + requires(!OffsetStorageType) void operator()( cudf::mutable_column_view offsetsView, cudf::size_type numRows, @@ -79,9 +77,8 @@ struct fillOffsetsForDecimalSumState { * @param stream CUDA stream for the launch. */ struct packDecimalSumState { - template < - typename SumT, - std::enable_if_t, int> = 0> + template + requires DecimalSumStorageType void operator()( cudf::column_view sumCol, const int64_t* counts, @@ -90,9 +87,8 @@ struct packDecimalSumState { cudf::size_type numRows, rmm::cuda_stream_view stream) const; - template < - typename SumT, - std::enable_if_t, int> = 0> + template + requires(!DecimalSumStorageType) void operator()( cudf::column_view sumCol, const int64_t* counts, @@ -113,9 +109,8 @@ struct packDecimalSumState { * @param stream CUDA stream for the launch. */ struct unpackDecimalSumState { - template < - typename OffsetT, - std::enable_if_t, int> = 0> + template + requires OffsetStorageType void operator()( cudf::column_view offsetsView, const uint8_t* chars, @@ -124,9 +119,8 @@ struct unpackDecimalSumState { cudf::size_type numRows, rmm::cuda_stream_view stream) const; - template < - typename OffsetT, - std::enable_if_t, int> = 0> + template + requires(!OffsetStorageType) void operator()( cudf::column_view offsetsView, const uint8_t* chars, @@ -147,9 +141,8 @@ struct unpackDecimalSumState { * @param stream CUDA stream for the launch. */ struct averageRoundDecimalSum { - template < - typename SumT, - std::enable_if_t, int> = 0> + template + requires DecimalSumStorageType void operator()( cudf::column_view sumCol, const int64_t* counts, @@ -157,9 +150,8 @@ struct averageRoundDecimalSum { cudf::size_type numRows, rmm::cuda_stream_view stream) const; - template < - typename SumT, - std::enable_if_t, int> = 0> + template + requires(!DecimalSumStorageType) void operator()( cudf::column_view sumCol, const int64_t* counts, From 06011d364861869f94662b84a0ffd3c65283af8c Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 12 Jun 2026 15:07:13 -0700 Subject: [PATCH 57/64] Refactor the aggregation kernel/functor structure to match that of the expression code in Part 4 --- .../cudf/exec/DecimalAggregationDevice.cu | 326 +++++++++--------- .../cudf/exec/DecimalAggregationDevice.h | 123 ++----- .../cudf/exec/DecimalAggregationState.cpp | 31 +- 3 files changed, 221 insertions(+), 259 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index ea56801d86e..9abdca261e2 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -30,6 +31,7 @@ #include #include +#include #include namespace facebook::velox::cudf_velox { @@ -128,61 +130,18 @@ struct AvgRoundFunctor { } }; -template -void launchFillOffsets( - cuda::std::span offsets, - rmm::cuda_stream_view stream) { - FillOffsetsFunctor op{offsets}; - cub::DeviceFor::ForEachN( - cuda::counting_iterator{0}, - static_cast(offsets.size()), - op, - stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchPackState( - cuda::std::span sums, - cuda::std::span counts, - cuda::std::span offsets, - uint8_t* chars, - rmm::cuda_stream_view stream) { - PackStateFunctor op{sums, counts, offsets, chars}; - cub::DeviceFor::ForEachN( - cuda::counting_iterator{0}, - static_cast(sums.size()), - op, - stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchUnpackState( - cuda::std::span offsets, - const uint8_t* chars, - cuda::std::span<__int128_t> sums, - cuda::std::span counts, - rmm::cuda_stream_view stream) { - UnpackStateFunctor op{offsets, chars, sums, counts}; - cub::DeviceFor::ForEachN( - cuda::counting_iterator{0}, - static_cast(sums.size()), - op, - stream.value()); - CUDF_CUDA_TRY(cudaGetLastError()); -} - -template -void launchAvgRound( - cuda::std::span sums, - cuda::std::span counts, - cuda::std::span out, +template +void launchDeviceFor( + cudf::size_type size, + BuildOp buildOp, rmm::cuda_stream_view stream) { - AvgRoundFunctor op{sums, counts, out}; + if (size == 0) { + return; + } + auto op = buildOp(); cub::DeviceFor::ForEachN( - cuda::counting_iterator{0}, - static_cast(out.size()), + cuda::counting_iterator{0}, + size, op, stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); @@ -234,132 +193,187 @@ std::pair buildStateValidityMaskImpl( namespace detail { -template - requires OffsetStorageType -void fillOffsetsForDecimalSumState::operator()( - cudf::mutable_column_view offsetsView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - launchFillOffsets( - cuda::std::span{ - offsetsView.data(), static_cast(numRows) + 1}, - stream); -} +template +concept OffsetStorageType = + std::same_as || std::same_as; -template void fillOffsetsForDecimalSumState::operator()( - cudf::mutable_column_view offsetsView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; -template void fillOffsetsForDecimalSumState::operator()( - cudf::mutable_column_view offsetsView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; +template +concept DecimalSumStorageType = + std::same_as || std::same_as; -template - requires OffsetStorageType -void unpackDecimalSumState::operator()( - cudf::column_view offsetsView, - const uint8_t* chars, - cudf::mutable_column_view sumView, - cudf::mutable_column_view countView, +template +concept ValidDecimalPackStorageTypes = + DecimalSumStorageType && OffsetStorageType; + +struct fillOffsetsForDecimalSumStateKernel { + cudf::mutable_column_view offsetsView; + cudf::size_type numRows; + rmm::cuda_stream_view stream; + + template + requires OffsetStorageType + void operator()() const { + launchDeviceFor( + numRows + 1, + [&] { + return FillOffsetsFunctor{cuda::std::span{ + offsetsView.data(), + static_cast(numRows) + 1}}; + }, + stream); + } + + template + requires(!OffsetStorageType) + void operator()() const { + CUDF_FAIL("Invalid offset type for decimal sum state"); + } +}; + +struct unpackDecimalSumStateKernel { + cudf::column_view offsetsView; + const uint8_t* chars; + cudf::mutable_column_view sumView; + cudf::mutable_column_view countView; + cudf::size_type numRows; + rmm::cuda_stream_view stream; + + template + requires OffsetStorageType + void operator()() const { + auto const n = static_cast(numRows); + launchDeviceFor( + numRows, + [&] { + return UnpackStateFunctor{ + cuda::std::span{offsetsView.data(), n}, + chars, + cuda::std::span<__int128_t>{sumView.data<__int128_t>(), n}, + cuda::std::span{countView.data(), n}}; + }, + stream); + } + + template + requires(!OffsetStorageType) + void operator()() const { + CUDF_FAIL("Invalid offset type for decimal sum state"); + } +}; + +struct averageRoundDecimalSumKernel { + cudf::column_view sumCol; + const int64_t* counts; + cudf::mutable_column_view outView; + cudf::size_type numRows; + rmm::cuda_stream_view stream; + + template + requires DecimalSumStorageType + void operator()() const { + auto const n = static_cast(numRows); + launchDeviceFor( + numRows, + [&] { + return AvgRoundFunctor{ + cuda::std::span{sumCol.data(), n}, + cuda::std::span{counts, n}, + cuda::std::span{outView.data(), n}}; + }, + stream); + } + + template + requires(!DecimalSumStorageType) + void operator()() const { + CUDF_FAIL("Invalid sum type for decimal average"); + } +}; + +struct packDecimalSumStateKernel { + cudf::column_view sumCol; + const int64_t* counts; + cudf::column_view offsetsView; + uint8_t* chars; + cudf::size_type numRows; + rmm::cuda_stream_view stream; + + template + requires ValidDecimalPackStorageTypes + void operator()() const { + auto const n = static_cast(numRows); + auto const sums = sumCol.data(); + launchDeviceFor( + numRows, + [&] { + return PackStateFunctor{ + cuda::std::span{sums, n}, + cuda::std::span{counts, n}, + cuda::std::span{offsetsView.data(), n}, + chars}; + }, + stream); + } + + template + requires(!ValidDecimalPackStorageTypes) + void operator()() const { + CUDF_FAIL("Invalid types for decimal sum state pack"); + } +}; + +void fillOffsetsForDecimalSumState( + cudf::type_id offsetType, + cudf::mutable_column_view offsetsView, cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - auto const n = static_cast(numRows); - launchUnpackState( - cuda::std::span{offsetsView.data(), n}, - chars, - cuda::std::span<__int128_t>{sumView.data<__int128_t>(), n}, - cuda::std::span{countView.data(), n}, - stream); + rmm::cuda_stream_view stream) { + cudf::type_dispatcher( + cudf::data_type{offsetType}, + fillOffsetsForDecimalSumStateKernel{offsetsView, numRows, stream}); } -template void unpackDecimalSumState::operator()( +void unpackDecimalSumState( + cudf::type_id offsetType, cudf::column_view offsetsView, const uint8_t* chars, cudf::mutable_column_view sumView, cudf::mutable_column_view countView, cudf::size_type numRows, - rmm::cuda_stream_view stream) const; -template void unpackDecimalSumState::operator()( - cudf::column_view offsetsView, - const uint8_t* chars, - cudf::mutable_column_view sumView, - cudf::mutable_column_view countView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; + rmm::cuda_stream_view stream) { + cudf::type_dispatcher( + cudf::data_type{offsetType}, + unpackDecimalSumStateKernel{ + offsetsView, chars, sumView, countView, numRows, stream}); +} -template - requires DecimalSumStorageType -void packDecimalSumState::operator()( +void averageRoundDecimalSum( + cudf::type_id sumType, cudf::column_view sumCol, const int64_t* counts, - cudf::column_view offsetsView, - uint8_t* chars, + cudf::mutable_column_view outView, cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - auto const n = static_cast(numRows); - auto const sums = sumCol.data(); - if (offsetsView.type().id() == cudf::type_id::INT32) { - launchPackState( - cuda::std::span{sums, n}, - cuda::std::span{counts, n}, - cuda::std::span{offsetsView.data(), n}, - chars, - stream); - } else { - launchPackState( - cuda::std::span{sums, n}, - cuda::std::span{counts, n}, - cuda::std::span{offsetsView.data(), n}, - chars, - stream); - } + rmm::cuda_stream_view stream) { + cudf::type_dispatcher( + cudf::data_type{sumType}, + averageRoundDecimalSumKernel{sumCol, counts, outView, numRows, stream}); } -template void packDecimalSumState::operator()( +void packDecimalSumState( + cudf::type_id sumType, + cudf::type_id offsetType, cudf::column_view sumCol, const int64_t* counts, cudf::column_view offsetsView, uint8_t* chars, cudf::size_type numRows, - rmm::cuda_stream_view stream) const; -template void packDecimalSumState::operator()<__int128_t>( - cudf::column_view sumCol, - const int64_t* counts, - cudf::column_view offsetsView, - uint8_t* chars, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; - -template - requires DecimalSumStorageType -void averageRoundDecimalSum::operator()( - cudf::column_view sumCol, - const int64_t* counts, - cudf::mutable_column_view outView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const { - auto const n = static_cast(numRows); - launchAvgRound( - cuda::std::span{sumCol.data(), n}, - cuda::std::span{counts, n}, - cuda::std::span{outView.data(), n}, - stream); + rmm::cuda_stream_view stream) { + cudf::double_type_dispatcher( + cudf::data_type{sumType}, + cudf::data_type{offsetType}, + packDecimalSumStateKernel{ + sumCol, counts, offsetsView, chars, numRows, stream}); } -template void averageRoundDecimalSum::operator()( - cudf::column_view sumCol, - const int64_t* counts, - cudf::mutable_column_view outView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; -template void averageRoundDecimalSum::operator()<__int128_t>( - cudf::column_view sumCol, - const int64_t* counts, - cudf::mutable_column_view outView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; - std::pair buildStateValidityMask( const cudf::column_view& sumCol, const cudf::column_view& countCol, diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.h b/velox/experimental/cudf/exec/DecimalAggregationDevice.h index 152b3652501..08cc37e4018 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.h +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.h @@ -22,21 +22,12 @@ #include #include -#include #include #include #include namespace facebook::velox::cudf_velox::detail { -template -concept OffsetStorageType = - std::same_as || std::same_as; - -template -concept DecimalSumStorageType = - std::same_as || std::same_as; - // Size in bytes of each row's packed decimal SUM intermediate state in the // strings payload (count, overflow placeholder, and 128-bit sum split into // words). @@ -45,30 +36,25 @@ constexpr size_t kDecimalSumStateSize = 32; /** * Writes strings-style prefix offsets: offset[i] == i * kDecimalSumStateSize. * + * @param offsetType INT32 or INT64; selects offset storage width via + * cudf::type_dispatcher. * @param offsetsView output offsets column of numRows + 1 elements. * @param numRows number of payload rows. * @param stream CUDA stream for the launch. */ -struct fillOffsetsForDecimalSumState { - template - requires OffsetStorageType - void operator()( - cudf::mutable_column_view offsetsView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; - - template - requires(!OffsetStorageType) - void operator()( - cudf::mutable_column_view offsetsView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const {} -}; +void fillOffsetsForDecimalSumState( + cudf::type_id offsetType, + cudf::mutable_column_view offsetsView, + cudf::size_type numRows, + rmm::cuda_stream_view stream); /** * Encodes each row's partial sum and count into the fixed-width device layout * used for VARBINARY interchange. * + * @param sumType DECIMAL64 or DECIMAL128; selects sum storage width. + * @param offsetType INT32 or INT64; selects offset storage width. sumType and + * offsetType are dispatched via cudf::double_type_dispatcher. * @param sumCol per-row sums. * @param counts per-row int64 counts. * @param offsetsView per-row byte offsets into chars. @@ -76,31 +62,21 @@ struct fillOffsetsForDecimalSumState { * @param numRows number of rows. * @param stream CUDA stream for the launch. */ -struct packDecimalSumState { - template - requires DecimalSumStorageType - void operator()( - cudf::column_view sumCol, - const int64_t* counts, - cudf::column_view offsetsView, - uint8_t* chars, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; - - template - requires(!DecimalSumStorageType) - void operator()( - cudf::column_view sumCol, - const int64_t* counts, - cudf::column_view offsetsView, - uint8_t* chars, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const {} -}; +void packDecimalSumState( + cudf::type_id sumType, + cudf::type_id offsetType, + cudf::column_view sumCol, + const int64_t* counts, + cudf::column_view offsetsView, + uint8_t* chars, + cudf::size_type numRows, + rmm::cuda_stream_view stream); /** * Inverse of packDecimalSumState. * + * @param offsetType INT32 or INT64; selects offset storage width via + * cudf::type_dispatcher. * @param offsetsView per-row byte offsets into chars. * @param chars packed payload buffer. * @param sumView output per-row DECIMAL128 sums. @@ -108,57 +84,34 @@ struct packDecimalSumState { * @param numRows number of rows. * @param stream CUDA stream for the launch. */ -struct unpackDecimalSumState { - template - requires OffsetStorageType - void operator()( - cudf::column_view offsetsView, - const uint8_t* chars, - cudf::mutable_column_view sumView, - cudf::mutable_column_view countView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; - - template - requires(!OffsetStorageType) - void operator()( - cudf::column_view offsetsView, - const uint8_t* chars, - cudf::mutable_column_view sumView, - cudf::mutable_column_view countView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const {} -}; +void unpackDecimalSumState( + cudf::type_id offsetType, + cudf::column_view offsetsView, + const uint8_t* chars, + cudf::mutable_column_view sumView, + cudf::mutable_column_view countView, + cudf::size_type numRows, + rmm::cuda_stream_view stream); /** * Per-row half-up integer divide of sum by count; count == 0 writes zero * (validity is applied separately). * + * @param sumType DECIMAL64 or DECIMAL128; selects sum storage width via + * cudf::type_dispatcher. * @param sumCol per-row sums. * @param counts per-row counts. * @param outView output per-row averages. * @param numRows number of rows. * @param stream CUDA stream for the launch. */ -struct averageRoundDecimalSum { - template - requires DecimalSumStorageType - void operator()( - cudf::column_view sumCol, - const int64_t* counts, - cudf::mutable_column_view outView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const; - - template - requires(!DecimalSumStorageType) - void operator()( - cudf::column_view sumCol, - const int64_t* counts, - cudf::mutable_column_view outView, - cudf::size_type numRows, - rmm::cuda_stream_view stream) const {} -}; +void averageRoundDecimalSum( + cudf::type_id sumType, + cudf::column_view sumCol, + const int64_t* counts, + cudf::mutable_column_view outView, + cudf::size_type numRows, + rmm::cuda_stream_view stream); /** * Builds a null mask for rows where sum and count are both valid and count is diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index 6238bb3a885..d7509f158f8 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include @@ -107,9 +106,8 @@ DecimalSumStateColumns deserializeDecimalSumState( offsetsType == cudf::type_id::INT64, "Decimal sum state requires INT32 or INT64 offsets (offset type is {})", cudf::type_to_name(offsetsView.type())); - cudf::type_dispatcher( - offsetsView.type(), - detail::unpackDecimalSumState{}, + detail::unpackDecimalSumState( + offsetsType, offsetsView, charsPtr, sumView, @@ -185,21 +183,18 @@ std::unique_ptr serializeDecimalSumState( static_cast(numRows) * detail::kDecimalSumStateSize, stream, mr); auto charsPtr = reinterpret_cast(charsBuf.data()); - cudf::type_dispatcher( - offsetsView.type(), - detail::fillOffsetsForDecimalSumState{}, - offsetsView, - rowCount, - stream); + detail::fillOffsetsForDecimalSumState( + offsetsType, offsetsView, rowCount, stream); + const auto sumType = sumCol.type().id(); VELOX_CHECK( - sumCol.type().id() == cudf::type_id::DECIMAL64 || - sumCol.type().id() == cudf::type_id::DECIMAL128, + sumType == cudf::type_id::DECIMAL64 || + sumType == cudf::type_id::DECIMAL128, "Unsupported decimal sum column type (type is {})", cudf::type_to_name(sumCol.type())); - cudf::type_dispatcher( - sumCol.type(), - detail::packDecimalSumState{}, + detail::packDecimalSumState( + sumType, + offsetsType, sumCol, countCol.data(), offsetsView, @@ -244,9 +239,9 @@ std::unique_ptr computeDecimalAverage( if (numRows > 0) { auto const rowCount = static_cast(numRows); - cudf::type_dispatcher( - sumCol.type(), - detail::averageRoundDecimalSum{}, + const auto sumType = sumCol.type().id(); + detail::averageRoundDecimalSum( + sumType, sumCol, countCol.data(), out->mutable_view(), From 9d1e8aaa46e14f98c7fa16518c74d91b7831c34b Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Fri, 12 Jun 2026 15:08:01 -0700 Subject: [PATCH 58/64] Format --- velox/experimental/cudf/exec/DecimalAggregationDevice.cu | 8 ++------ velox/experimental/cudf/exec/DecimalAggregationState.cpp | 8 +------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu index 9abdca261e2..16d26df831b 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationDevice.cu +++ b/velox/experimental/cudf/exec/DecimalAggregationDevice.cu @@ -140,10 +140,7 @@ void launchDeviceFor( } auto op = buildOp(); cub::DeviceFor::ForEachN( - cuda::counting_iterator{0}, - size, - op, - stream.value()); + cuda::counting_iterator{0}, size, op, stream.value()); CUDF_CUDA_TRY(cudaGetLastError()); } @@ -217,8 +214,7 @@ struct fillOffsetsForDecimalSumStateKernel { numRows + 1, [&] { return FillOffsetsFunctor{cuda::std::span{ - offsetsView.data(), - static_cast(numRows) + 1}}; + offsetsView.data(), static_cast(numRows) + 1}}; }, stream); } diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index d7509f158f8..11c145e3ee2 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -107,13 +107,7 @@ DecimalSumStateColumns deserializeDecimalSumState( "Decimal sum state requires INT32 or INT64 offsets (offset type is {})", cudf::type_to_name(offsetsView.type())); detail::unpackDecimalSumState( - offsetsType, - offsetsView, - charsPtr, - sumView, - countView, - numRows, - stream); + offsetsType, offsetsView, charsPtr, sumView, countView, numRows, stream); if (stateCol.nullable()) { auto nullMask = cudf::copy_bitmask(stateCol, stream, mr); From 1fffd03b97ab65c78df2ecfaf785d366eb96ef13 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 16 Jun 2026 11:50:59 -0700 Subject: [PATCH 59/64] Use get_temp_mr() in castCountColumnToInt64 and change function comments to Doxygen style, per @devavret --- .../cudf/exec/DecimalAggregationHostOps.cpp | 9 +-- .../cudf/exec/DecimalAggregationHostOps.h | 78 +++++++++++++------ .../cudf/exec/DecimalAggregationState.h | 58 ++++++++++---- 3 files changed, 101 insertions(+), 44 deletions(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp index 428b916e2cc..91561dfa0a1 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp @@ -53,11 +53,10 @@ cudf::column_view castDecimal64InputToDecimal128( std::unique_ptr castCountColumnToInt64( std::unique_ptr count, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr) { + rmm::cuda_stream_view stream) { if (count->type().id() != cudf::type_id::INT64) { count = - cudf::cast(*count, cudf::data_type{cudf::type_id::INT64}, stream, mr); + cudf::cast(*count, cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); } return count; } @@ -67,7 +66,7 @@ std::unique_ptr serializeDecimalPartialOrIntermediateState( std::unique_ptr count, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { - count = castCountColumnToInt64(std::move(count), stream, mr); + count = castCountColumnToInt64(std::move(count), stream); return serializeDecimalSumState(sum->view(), count->view(), stream, mr); } @@ -77,7 +76,7 @@ std::unique_ptr finalizeDecimalAverage( const TypePtr& resultType, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { - count = castCountColumnToInt64(std::move(count), stream, mr); + count = castCountColumnToInt64(std::move(count), stream); auto avgCol = computeDecimalAverage(sum->view(), count->view(), stream, mr); auto const cudfOutType = veloxToCudfDataType(resultType); if (avgCol->type() != cudfOutType) { diff --git a/velox/experimental/cudf/exec/DecimalAggregationHostOps.h b/velox/experimental/cudf/exec/DecimalAggregationHostOps.h index f3be2cfe876..778f9120e78 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationHostOps.h +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.h @@ -26,44 +26,78 @@ namespace facebook::velox::cudf_velox { -// Asserts that a column holds serialized decimal aggregate state in the form -// Velox uses for VARBINARY: a cuDF STRING column whose bytes are the packed -// sum/count payloads (see serializeDecimalSumState). The payload does not carry -// scale, so VARBINARY intermediate steps decode at scale 0; the real scale is -// applied at final cast time. +/** + * Asserts that a column holds serialized decimal aggregate state in the form + * Velox uses for VARBINARY: a cuDF STRING column whose bytes are the packed + * sum/count payloads (see serializeDecimalSumState). The payload does not carry + * scale, so VARBINARY intermediate steps decode at scale 0; the real scale is + * applied at final cast time. + * + * @param column column to validate. + */ void validateIntermediateColumnType(cudf::column_view const& column); -// Casts a DECIMAL64 column up to DECIMAL128 (scale preserved) so a subsequent -// SUM accumulates in 128 bits instead of wrapping. Allocates the casted column -// from the temporary memory resource into holder and returns its view. -// Lifetime stays valid only while holder is alive. +/** + * Casts a DECIMAL64 column up to DECIMAL128 (scale preserved) so a subsequent + * SUM accumulates in 128 bits instead of wrapping. Allocates the casted column + * from the temporary memory resource into holder and returns its view. Lifetime + * stays valid only while holder is alive. + * + * @param inputCol DECIMAL64 input column. + * @param holder receives ownership of the casted column when inputCol is + * DECIMAL64; unchanged otherwise. + * @param stream CUDA stream for device work. + * @return view of inputCol or of the column stored in holder. + */ cudf::column_view castDecimal64InputToDecimal128( cudf::column_view inputCol, std::unique_ptr& holder, rmm::cuda_stream_view stream); -// Ensures the partial-row count column is INT64, casting with the temporary -// memory resource (the result is consumed internally, not part of operator -// output) when the incoming type differs. +/** + * Ensures the partial-row count column is INT64, casting with the temporary + * memory resource (the result is consumed internally, not part of operator + * output) when the incoming type differs. + * + * @param count partial-row count column. + * @param stream CUDA stream for device work. + * @return INT64 count column (moved through when already INT64). + */ std::unique_ptr castCountColumnToInt64( std::unique_ptr count, - rmm::cuda_stream_view stream, - rmm::device_async_resource_ref mr); + rmm::cuda_stream_view stream); -// Normalizes the count column to INT64, then encodes sum and count into a -// single STRING column of fixed-width per-row payloads (delegates to -// serializeDecimalSumState). Used when emitting or persisting partial / -// intermediate decimal SUM state for the cuDF path. +/** + * Normalizes the count column to INT64, then encodes sum and count into a + * single STRING column of fixed-width per-row payloads (delegates to + * serializeDecimalSumState). Used when emitting or persisting partial / + * intermediate decimal SUM state for the cuDF path. + * + * @param sum partial sum column (DECIMAL64 or DECIMAL128). + * @param count partial-row count column. + * @param stream CUDA stream for device work. + * @param mr memory resource for allocated columns. + * @return STRING column of serialized state. + */ std::unique_ptr serializeDecimalPartialOrIntermediateState( std::unique_ptr sum, std::unique_ptr count, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); -// Normalizes the count column to INT64, computes a per-row decimal average -// from intermediate sum/count (delegates to computeDecimalAverage), then casts -// the result to the Velox result type when its cuDF decimal encoding differs -// from the average column's type. +/** + * Normalizes the count column to INT64, computes a per-row decimal average + * from intermediate sum/count (delegates to computeDecimalAverage), then casts + * the result to the Velox result type when its cuDF decimal encoding differs + * from the average column's type. + * + * @param sum intermediate sum column (DECIMAL64 or DECIMAL128). + * @param count intermediate count column. + * @param resultType Velox type of the finalized average. + * @param stream CUDA stream for device work. + * @param mr memory resource for allocated columns. + * @return finalized average column. + */ std::unique_ptr finalizeDecimalAverage( std::unique_ptr sum, std::unique_ptr count, diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.h b/velox/experimental/cudf/exec/DecimalAggregationState.h index e184470a34a..3813d58f45e 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.h +++ b/velox/experimental/cudf/exec/DecimalAggregationState.h @@ -29,34 +29,58 @@ struct DecimalSumStateColumns { std::unique_ptr count; }; -// Decodes intermediate decimal SUM aggregate state stored as a cuDF STRING -// column (fixed-size packed bytes per row, converted from Velox VARBINARY) into -// two device columns: a DECIMAL128 sum with scale -scale (matching Velox -// intermediate state) and an INT64 partial row count. Handles empty input, -// all-null state without touching payload buffers, and propagates the source -// null mask to both outputs when present. +/** + * Decodes intermediate decimal SUM aggregate state stored as a cuDF STRING + * column (fixed-size packed bytes per row, converted from Velox VARBINARY) into + * two device columns: a DECIMAL128 sum with scale -scale (matching Velox + * intermediate state) and an INT64 partial row count. Handles empty input, + * all-null state without touching payload buffers, and propagates the source + * null mask to both outputs when present. + * + * @param stateCol STRING column of packed sum/count payloads. + * @param scale decimal scale used to set the output sum column's scale to + * -scale. + * @param stream CUDA stream for device work. + * @return decoded sum and count columns. + */ DecimalSumStateColumns deserializeDecimalSumState( const cudf::column_view& stateCol, int32_t scale, rmm::cuda_stream_view stream); -// Encodes partial decimal SUM state (DECIMAL64 or DECIMAL128 sums plus -// INT64 counts) into a single STRING column (later converted to Velox -// VARBINARY): per-row fixed-width payloads and string offsets (INT32 or INT64 -// depending on total char size and cuDF large-strings settings). The output -// null mask matches buildStateValidityMask: a row is invalid if the sum or -// count is null, or the count is zero. +/** + * Encodes partial decimal SUM state (DECIMAL64 or DECIMAL128 sums plus INT64 + * counts) into a single STRING column (later converted to Velox VARBINARY): + * per-row fixed-width payloads and string offsets (INT32 or INT64 depending on + * total char size and cuDF large-strings settings). The output null mask + * matches buildStateValidityMask: a row is invalid if the sum or count is null, + * or the count is zero. + * + * @param sumCol per-row partial sums (DECIMAL64 or DECIMAL128). + * @param countCol per-row INT64 partial row counts. + * @param stream CUDA stream for device work. + * @param mr memory resource for allocated columns. + * @return STRING column of serialized state. + */ std::unique_ptr serializeDecimalSumState( const cudf::column_view& sumCol, const cudf::column_view& countCol, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr); -// Finalizes AVG from intermediate SUM state: divides each sum by its count -// on device with decimal-specific rounding (see averageRoundDecimalSum), -// producing a column of the same decimal type as the sum. Rows are null -// where buildStateValidityMask marks them invalid (null sum/count or zero -// count), matching serializeDecimalSumState. +/** + * Finalizes AVG from intermediate SUM state: divides each sum by its count on + * device with decimal-specific rounding (see averageRoundDecimalSum), + * producing a column of the same decimal type as the sum. Rows are null where + * buildStateValidityMask marks them invalid (null sum/count or zero count), + * matching serializeDecimalSumState. + * + * @param sumCol per-row partial sums (DECIMAL64 or DECIMAL128). + * @param countCol per-row INT64 partial row counts. + * @param stream CUDA stream for device work. + * @param mr memory resource for allocated columns. + * @return per-row decimal average column. + */ std::unique_ptr computeDecimalAverage( const cudf::column_view& sumCol, const cudf::column_view& countCol, From 35bd662a88f1fa42f0feae5201f08f04e3311d13 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 16 Jun 2026 11:54:07 -0700 Subject: [PATCH 60/64] Use cudf::type_to_name, per @karthikeyann --- velox/experimental/cudf/exec/DecimalAggregationState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index 11c145e3ee2..c66a0d25bc0 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -131,7 +131,7 @@ std::unique_ptr serializeDecimalSumState( VELOX_CHECK( countCol.type().id() == cudf::type_id::INT64, "Decimal sum state requires INT64 count column (type is {})", - static_cast(countCol.type().id())); + cudf::type_to_name(countCol.type())); auto numRows = sumCol.size(); VELOX_CHECK_EQ( numRows, From 80c1cce487561f6e23fc7cafc0bae9d092393fca Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 16 Jun 2026 11:54:24 -0700 Subject: [PATCH 61/64] Fix comparison, per @karthikeyann --- velox/experimental/cudf/exec/CudfReduce.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index 38caf1cbaa9..e9098217604 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -472,7 +472,7 @@ struct ReduceDecimalAvgAggregator : ReduceAggregator { return reduceIntermediateDecimalFromSerializedColumn( inputCol, outputType, stream, mr); case core::AggregationNode::Step::kFinal: - VELOX_CHECK(outputType == resultType, "outputType/resultType mismatch"); + VELOX_CHECK(*outputType == *resultType, "outputType/resultType mismatch"); return reduceFinalDecimalAvgFromSerializedColumn( inputCol, outputType, stream, mr); default: From db9c80248031ed1760a800c1cd2aa0c3e9e3e2b9 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 16 Jun 2026 11:58:13 -0700 Subject: [PATCH 62/64] Add decimal sum state payload size check, per @karthikeyann --- velox/experimental/cudf/exec/DecimalAggregationState.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/velox/experimental/cudf/exec/DecimalAggregationState.cpp b/velox/experimental/cudf/exec/DecimalAggregationState.cpp index c66a0d25bc0..523e23613d8 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationState.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationState.cpp @@ -80,6 +80,15 @@ DecimalSumStateColumns deserializeDecimalSumState( cudf::strings_column_view strings(stateCol); + auto const payloadSize = strings.chars_size(stream); + auto const expectedPayloadSize = + static_cast(numRows) * detail::kDecimalSumStateSize; + VELOX_CHECK( + payloadSize == expectedPayloadSize, + "Decimal sum state requires payload size {} (got {})", + expectedPayloadSize, + payloadSize); + auto offsetsView = strings.offsets(); auto charsPtr = reinterpret_cast(strings.chars_begin(stream)); From 8aa0e2b770f5bc116052eb4c2747827fed3c17c3 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 16 Jun 2026 12:11:53 -0700 Subject: [PATCH 63/64] Combine conditional branches, per @karthikeyann --- velox/experimental/cudf/exec/CudfGroupby.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 224f1b04fde..8eacf75f839 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -283,18 +283,7 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { cudf::table_view const& tbl, std::vector& requests, rmm::cuda_stream_view stream) override { - if (step == core::AggregationNode::Step::kIntermediate) { - addDecimalDecodedSumCountRequests( - tbl, - inputIndex, - resultType, - requests, - stream, - sumIdx_, - countIdx_, - decodedSum_, - decodedCount_); - } else if (step == core::AggregationNode::Step::kFinal) { + if (step == core::AggregationNode::Step::kIntermediate || step == core::AggregationNode::Step::kFinal) { addDecimalDecodedSumCountRequests( tbl, inputIndex, From 0c85fa1859a27dbcefc67a0436b5146b6d5ff997 Mon Sep 17 00:00:00 2001 From: Simon Eves Date: Tue, 16 Jun 2026 12:18:17 -0700 Subject: [PATCH 64/64] Format --- velox/experimental/cudf/exec/CudfGroupby.cpp | 3 ++- velox/experimental/cudf/exec/CudfReduce.cpp | 3 ++- velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/velox/experimental/cudf/exec/CudfGroupby.cpp b/velox/experimental/cudf/exec/CudfGroupby.cpp index 8eacf75f839..172bf0e8456 100644 --- a/velox/experimental/cudf/exec/CudfGroupby.cpp +++ b/velox/experimental/cudf/exec/CudfGroupby.cpp @@ -283,7 +283,8 @@ struct GroupbyDecimalAvgAggregator : GroupbyAggregator { cudf::table_view const& tbl, std::vector& requests, rmm::cuda_stream_view stream) override { - if (step == core::AggregationNode::Step::kIntermediate || step == core::AggregationNode::Step::kFinal) { + if (step == core::AggregationNode::Step::kIntermediate || + step == core::AggregationNode::Step::kFinal) { addDecimalDecodedSumCountRequests( tbl, inputIndex, diff --git a/velox/experimental/cudf/exec/CudfReduce.cpp b/velox/experimental/cudf/exec/CudfReduce.cpp index e9098217604..ef2e2cdc701 100644 --- a/velox/experimental/cudf/exec/CudfReduce.cpp +++ b/velox/experimental/cudf/exec/CudfReduce.cpp @@ -472,7 +472,8 @@ struct ReduceDecimalAvgAggregator : ReduceAggregator { return reduceIntermediateDecimalFromSerializedColumn( inputCol, outputType, stream, mr); case core::AggregationNode::Step::kFinal: - VELOX_CHECK(*outputType == *resultType, "outputType/resultType mismatch"); + VELOX_CHECK( + *outputType == *resultType, "outputType/resultType mismatch"); return reduceFinalDecimalAvgFromSerializedColumn( inputCol, outputType, stream, mr); default: diff --git a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp index 91561dfa0a1..73bff493087 100644 --- a/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp +++ b/velox/experimental/cudf/exec/DecimalAggregationHostOps.cpp @@ -55,8 +55,8 @@ std::unique_ptr castCountColumnToInt64( std::unique_ptr count, rmm::cuda_stream_view stream) { if (count->type().id() != cudf::type_id::INT64) { - count = - cudf::cast(*count, cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); + count = cudf::cast( + *count, cudf::data_type{cudf::type_id::INT64}, stream, get_temp_mr()); } return count; }