Skip to content

Commit ec4a72c

Browse files
committed
Expression executor can now handle mixed input columns lengths: some empty, some full
1 parent 4860082 commit ec4a72c

4 files changed

Lines changed: 119 additions & 46 deletions

File tree

src/expression_executor/gpu_expression_executor.cpp

Lines changed: 107 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ void GpuExpressionExecutor::Initialize(const Expression& expr, GpuExpressionExec
5454

5555
void GpuExpressionExecutor::SetInputColumns(const GPUIntermediateRelation& input_relation)
5656
{
57+
input_count = 0;
58+
has_null_input_column = false;
59+
5760
// Shallow copy the columns
5861
input_columns = input_relation.columns;
5962

@@ -64,62 +67,140 @@ void GpuExpressionExecutor::SetInputColumns(const GPUIntermediateRelation& input
6467
}
6568
else
6669
{
67-
// All columns should have the same count
68-
//TODO: This is assuming that all columns have the same size, which is not always true if the pipeline source is the hash table from RIGHT join
69-
const auto col = input_columns[0];
70-
// The input column may be null, in which case the expression evaluation should be a no-op
71-
input_count = col == nullptr ? 0
72-
: col->row_ids == nullptr ? static_cast<cudf::size_type>(col->column_length)
73-
: static_cast<cudf::size_type>(col->row_id_count);
70+
// All columns that are not null should have the same count
71+
for (const auto& col : input_columns)
72+
{
73+
const auto temp_count = col == nullptr ? 0
74+
: col->row_ids == nullptr
75+
? static_cast<cudf::size_type>(col->column_length)
76+
: static_cast<cudf::size_type>(col->row_id_count);
77+
if (temp_count > 0)
78+
{
79+
input_count = temp_count;
80+
}
81+
else
82+
{
83+
has_null_input_column = true;
84+
}
85+
}
86+
}
87+
}
88+
89+
// Helper template function for HasNullLeaf()
90+
template <typename ExpressionT>
91+
bool GpuExpressionExecutor::HasNullLeafLoop(const ExpressionT& expr) const
92+
{
93+
for (const auto& child : expr.children)
94+
{
95+
if (HasNullLeaf(*child))
96+
{
97+
return true;
98+
}
99+
}
100+
return false;
101+
}
102+
103+
bool GpuExpressionExecutor::HasNullLeaf(const Expression& expr) const
104+
{
105+
// Check if the expression is a null reference
106+
switch (expr.GetExpressionClass())
107+
{
108+
case ExpressionClass::BOUND_BETWEEN: {
109+
const auto& between_expr = expr.Cast<BoundBetweenExpression>();
110+
return HasNullLeaf(*between_expr.input) || HasNullLeaf(*between_expr.lower) ||
111+
HasNullLeaf(*between_expr.upper);
112+
}
113+
case ExpressionClass::BOUND_CASE: {
114+
const auto& case_expr = expr.Cast<BoundCaseExpression>();
115+
for (const auto& case_check : case_expr.case_checks)
116+
{
117+
if (HasNullLeaf(*case_check.when_expr) || HasNullLeaf(*case_check.then_expr))
118+
{
119+
return true;
120+
}
121+
}
122+
return HasNullLeaf(*case_expr.else_expr);
123+
}
124+
case ExpressionClass::BOUND_CAST: {
125+
const auto& cast_expr = expr.Cast<BoundCastExpression>();
126+
return HasNullLeaf(*cast_expr.child);
127+
}
128+
case ExpressionClass::BOUND_COMPARISON: {
129+
const auto& comp_expr = expr.Cast<BoundComparisonExpression>();
130+
return HasNullLeaf(*comp_expr.left) || HasNullLeaf(*comp_expr.right);
131+
}
132+
case ExpressionClass::BOUND_CONJUNCTION: {
133+
return HasNullLeafLoop(expr.Cast<BoundConjunctionExpression>());
134+
}
135+
case ExpressionClass::BOUND_CONSTANT: {
136+
// Base case
137+
return false;
138+
}
139+
case ExpressionClass::BOUND_FUNCTION: {
140+
return HasNullLeafLoop(expr.Cast<BoundFunctionExpression>());
141+
}
142+
case ExpressionClass::BOUND_OPERATOR: {
143+
return HasNullLeafLoop(expr.Cast<BoundOperatorExpression>());
144+
}
145+
case ExpressionClass::BOUND_REF: {
146+
// Base case
147+
const auto& ref_expr = expr.Cast<BoundReferenceExpression>();
148+
const auto& col = input_columns[ref_expr.index];
149+
return col == nullptr || col->data_wrapper.data == nullptr;
150+
}
151+
default:
152+
throw InternalException("HasNullLeaf called on an expression [" + expr.ToString() +
153+
"] with unsupported expression class!");
74154
}
155+
return false;
75156
}
76157

77158
void GpuExpressionExecutor::Execute(const GPUIntermediateRelation& input_relation,
78159
GPUIntermediateRelation& output_relation)
79160
{
80-
D_ASSERT(expressions.size() == output_relation.columns);
161+
D_ASSERT(expressions.size() == output_relation.columns.size());
81162
D_ASSERT(!expressions.empty());
163+
82164
SetInputColumns(input_relation);
83165

84166
// Loop over expressions to execute
85167
for (idx_t i = 0; i < expressions.size(); ++i)
86168
{
169+
const auto& expr = *expressions[i];
170+
87171
// If the expression is a reference, just pass it through
88-
if (expressions[i]->expression_class == ExpressionClass::BOUND_REF)
172+
if (expr.expression_class == ExpressionClass::BOUND_REF)
89173
{
90-
auto input_idx = expressions[i]->Cast<BoundReferenceExpression>().index;
174+
auto input_idx = expr.Cast<BoundReferenceExpression>().index;
91175
output_relation.columns[i] = input_relation.columns[input_idx];
92176
continue;
93177
}
94178

95-
// Make placeholder column
179+
// Make placeholder output column
96180
output_relation.columns[i] =
97-
make_shared_ptr<GPUColumn>(0,
98-
convertLogicalTypeToColumnType(expressions[i]->return_type),
99-
nullptr);
181+
make_shared_ptr<GPUColumn>(0, convertLogicalTypeToColumnType(expr.return_type), nullptr);
100182

101-
// If input count is zero, no-op
102-
if (input_count == 0)
183+
// Skip execution if the input count is zero or if there is a null leaf
184+
if (input_count == 0 || (has_null_input_column && HasNullLeaf(expr)))
103185
{
104186
continue;
105187
}
106188

107-
// Execute the expression
189+
// Otherwise, execute the expression
108190
auto result = ExecuteExpression(i);
109191

110192
// Cast the `result` from libcudf to `return_type` if `result` has different types.
111193
// E.g., `extract(year from col)` from libcudf returns int16_t but duckdb requires int64_t
112194
auto cudf_return_type = GpuExpressionState::GetCudfType(expressions[i]->return_type);
113-
if (result->type().id() != cudf_return_type.id()) {
114-
result = cudf::cast(result->view(),
115-
cudf_return_type,
116-
cudf::get_default_stream(),
117-
resource_ref);
195+
if (result->type().id() != cudf_return_type.id())
196+
{
197+
result =
198+
cudf::cast(result->view(), cudf_return_type, cudf::get_default_stream(), resource_ref);
118199
}
119200

120201
// Transfer to output relation (zero copy)
121202
output_relation.columns[i]->setFromCudfColumn(*result,
122-
false,
203+
false, // How to know?
123204
nullptr,
124205
0,
125206
&GPUBufferManager::GetInstance());
@@ -134,12 +215,12 @@ void GpuExpressionExecutor::Select(GPUIntermediateRelation& input_relation,
134215

135216
SetInputColumns(input_relation);
136217

137-
// If input count is zero, no-op
138-
if (input_count == 0)
218+
// If the input count is zero or if there is a null leaf, just materialize
219+
if (input_count == 0 || (has_null_input_column && HasNullLeaf(*expressions[0])))
139220
{
140221
HandleMaterializeRowIDs(input_relation,
141222
output_relation,
142-
input_count,
223+
0,
143224
nullptr,
144225
&GPUBufferManager::GetInstance(),
145226
true);

src/expression_executor/specializations/gpu_execute_comparison.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ struct ComparisonDispatcher
9393
return_type);
9494
case cudf::type_id::FLOAT32:
9595
return DoScalarComparison<float_t>(left->view(),
96-
right_value.GetValue<float>(),
96+
right_value.GetValue<float_t>(),
9797
return_type);
9898
case cudf::type_id::FLOAT64:
9999
return DoScalarComparison<double_t>(left->view(),
100-
right_value.GetValue<double>(),
100+
right_value.GetValue<double_t>(),
101101
return_type);
102102
case cudf::type_id::BOOL8:
103103
return DoScalarComparison<bool>(left->view(), right_value.GetValue<bool>(), return_type);

src/include/expression_executor/gpu_dispatcher.hpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ enum class StringMatchingType : uint8_t
2222
//----------Gpu Dispatcher----------//
2323
struct GpuDispatcher
2424
{
25-
//----------Materialize----------//
25+
//----------Materialize----------//
2626
static std::unique_ptr<cudf::column> DispatchMaterialize(const GPUColumn* input,
2727
rmm::device_async_resource_ref mr);
2828

@@ -33,30 +33,14 @@ struct GpuDispatcher
3333
rmm::device_async_resource_ref mr);
3434

3535
//----------String Matching----------//
36-
template<StringMatchingType MatchType>
36+
template <StringMatchingType MatchType>
3737
static std::unique_ptr<cudf::column> DispatchStringMatching(const cudf::column_view& input,
3838
const std::string& match_str,
3939
rmm::device_async_resource_ref mr);
4040

4141
//----------Selection----------//
4242
static std::tuple<uint64_t*, uint64_t> DispatchSelect(const cudf::column_view& bitmap,
4343
rmm::device_async_resource_ref mr);
44-
45-
//----------Utilities----------//
46-
// For substring
47-
static std::unique_ptr<cudf::column> MakeColumnFromPtrs(char* data,
48-
uint64_t* offsets,
49-
uint64_t count,
50-
rmm::device_async_resource_ref mr);
51-
// For matching
52-
static std::unique_ptr<cudf::column> MakeColumnFromRowIds(uint64_t* row_ids,
53-
uint64_t row_id_count,
54-
uint64_t input_count,
55-
rmm::device_async_resource_ref mr);
56-
57-
// For extracting data from cudf::column types for sirius string APIs
58-
static std::tuple<uint64_t, char*, uint64_t*, uint64_t>
59-
PrepareStringData(const cudf::column_view& input);
6044
};
6145

6246
} // namespace sirius

src/include/expression_executor/gpu_expression_executor.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ struct GpuExpressionExecutor
4646
rmm::device_async_resource_ref resource_ref = GPUBufferManager::GetInstance().mr;
4747
// The input count for the current relation (needed for materializing constants)
4848
cudf::size_type input_count;
49+
// Whether some input column is empty
50+
bool has_null_input_column;
4951
// Static flag indicating whether to use cudf or sirius for string functions
5052
static constexpr bool use_cudf = USE_CUDF_EXPR;
5153

@@ -59,6 +61,12 @@ struct GpuExpressionExecutor
5961
// Set the input count and columns for the expression executor
6062
void SetInputColumns(const GPUIntermediateRelation& input_relation);
6163

64+
// Before evaluating an expression, check the leaves for nullptrs
65+
// (Assumes the input columns have already been set)
66+
bool HasNullLeaf(const Expression& expr) const;
67+
template <typename ExpressionT>
68+
bool HasNullLeafLoop(const ExpressionT& expr) const;
69+
6270
// Execute the set of expressions with the given input relation and store the result in the output
6371
// relation (Provides the main interface with client code for Projections).
6472
void Execute(const GPUIntermediateRelation& input_relation,

0 commit comments

Comments
 (0)