Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -740,11 +740,16 @@ add_library(
src/join/conditional_join.cu
src/join/cross_join.cu
src/join/distinct_hash_join.cu
src/join/filter_join_indices.cu
src/join/filter_join_indices_kernel_complex.cu
src/join/filter_join_indices_kernel_null_complex.cu
src/join/filter_join_indices_kernel_null_primitive.cu
src/join/filter_join_indices_kernel_primitive.cu
src/join/filter_join_indices/filter_join_indices.cu
src/join/filter_join_indices/filter_join_indices_jit.cu
src/join/filter_join_indices/filter_join_indices_kernel_complex.cu
src/join/filter_join_indices/filter_join_indices_kernel_null_complex.cu
src/join/filter_join_indices/filter_join_indices_kernel_null_primitive.cu
src/join/filter_join_indices/filter_join_indices_kernel_primitive.cu
src/join/filter_join_indices/filter_join_indices_output_size_kernel_complex.cu
src/join/filter_join_indices/filter_join_indices_output_size_kernel_null_complex.cu
src/join/filter_join_indices/filter_join_indices_output_size_kernel_null_primitive.cu
src/join/filter_join_indices/filter_join_indices_output_size_kernel_primitive.cu
src/join/filtered_join.cu
src/join/hash_join/finalize_partitioned_full_join.cpp
src/join/hash_join/full_join_match_context.cpp
Expand All @@ -767,11 +772,10 @@ add_library(
src/join/hash_join/partitioned_left_join.cu
src/join/hash_join/partitioned_retrieve.cu
src/join/hash_join/partitioned_retrieve_outer.cu
src/join/mark_join.cu
src/join/filter_join_indices_jit.cu
src/join/join.cu
src/join/join_utils.cu
src/join/key_remapping.cu
src/join/mark_join.cu
src/join/mixed_join.cu
src/join/mixed_join_kernel.cu
src/join/mixed_join_kernel_nulls.cu
Expand Down
39 changes: 39 additions & 0 deletions cpp/include/cudf/join/join.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ std::unique_ptr<cudf::table> cross_join(
*
* @throw std::invalid_argument if join_kind is not INNER_JOIN, LEFT_JOIN, or FULL_JOIN.
* @throw std::invalid_argument if left_indices and right_indices have different sizes.
* @throw std::invalid_argument if predicate does not produce a Boolean output.
*
* @param left The left table for predicate evaluation (conditional columns only).
* @param right The right table for predicate evaluation (conditional columns only).
Expand All @@ -347,6 +348,44 @@ filter_join_indices(cudf::table_view const& left,
rmm::cuda_stream_view stream = cudf::get_default_stream(),
rmm::device_async_resource_ref mr = cudf::get_current_device_resource_ref());

/**
* @brief Returns the exact output size of `filter_join_indices` without materializing
* the filtered index vectors.
*
* Runs the same predicate evaluation as `filter_join_indices` but skips the index
* materialization step, returning only the total number of pairs that would be
* emitted. The semantics per `join_kind` match `filter_join_indices`:
* - INNER_JOIN: number of pairs where the predicate evaluates to true.
* - LEFT_JOIN: predicate-passing pairs plus one entry per left row with no passing match.
* - FULL_JOIN: input pairs plus one extra entry per pair whose predicate failed
* (because failed matches split into `(left, JoinNoMatch)` and `(JoinNoMatch, right)`).
*
* The returned size may be passed as a precomputed hint to APIs that compose
* `filter_join_indices` (for example, the mixed join APIs).
*
* @throw std::invalid_argument if `join_kind` is not INNER_JOIN, LEFT_JOIN, or FULL_JOIN.
* @throw std::invalid_argument if `left_indices` and `right_indices` have different sizes.
Comment thread
PointKernel marked this conversation as resolved.
* @throw std::invalid_argument if `predicate` does not produce a Boolean output.
*
* @param left The left table for predicate evaluation (conditional columns only).
* @param right The right table for predicate evaluation (conditional columns only).
* @param left_indices Device span of row indices in the left table.
* @param right_indices Device span of row indices in the right table.
* @param predicate An AST expression that returns a boolean for each pair of rows.
* @param join_kind The type of join operation. Must be INNER_JOIN, LEFT_JOIN, or FULL_JOIN.
* @param stream CUDA stream used for kernel launches and memory operations.
*
* @return The exact number of pairs that `filter_join_indices` would produce.
*/
[[nodiscard]] std::size_t filter_join_indices_output_size(
cudf::table_view const& left,
cudf::table_view const& right,
cudf::device_span<size_type const> left_indices,
cudf::device_span<size_type const> right_indices,
cudf::ast::expression const& predicate,
cudf::join_kind join_kind,
rmm::cuda_stream_view stream = cudf::get_default_stream());

/**
* @brief JIT-based filtering of join result indices using string predicate.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include "filter_join_indices_kernel.cuh"
#include "join/filter_join_indices/filter_join_indices_kernel.cuh"
#include "join/filter_join_indices/filter_join_indices_output_size_kernel.hpp"
#include "join/join_common_utils.hpp"

#include <cudf/ast/detail/expression_parser.hpp>
#include <cudf/ast/expressions.hpp>
Expand All @@ -13,7 +15,9 @@
#include <cudf/detail/iterator.cuh>
#include <cudf/detail/nvtx/ranges.hpp>
#include <cudf/detail/utilities/cuda.cuh>
#include <cudf/detail/utilities/dispatchers.hpp>
#include <cudf/detail/utilities/grid_1d.cuh>
#include <cudf/detail/utilities/vector_factories.hpp>
#include <cudf/join/join.hpp>
#include <cudf/table/table_device_view.cuh>
#include <cudf/table/table_view.hpp>
Expand All @@ -31,6 +35,7 @@
#include <cuco/static_set.cuh>
#include <cuda/functional>
#include <cuda/iterator>
#include <cuda/std/functional>
#include <cuda/std/tuple>
#include <thrust/iterator/zip_iterator.h>

Expand Down Expand Up @@ -76,7 +81,8 @@ filter_join_indices(cudf::table_view const& left,
predicate, left, right, has_nulls, stream, cudf::get_current_device_resource_ref()};

CUDF_EXPECTS(parser.output_type().id() == type_id::BOOL8,
"The predicate expression must produce a Boolean output");
"The predicate expression must produce a Boolean output",
std::invalid_argument);

// Check if expression contains complex types
auto const has_complex_type = parser.has_complex_type();
Expand Down Expand Up @@ -355,6 +361,88 @@ filter_join_indices(cudf::table_view const& left,
}
}

std::size_t filter_join_indices_output_size(cudf::table_view const& left,
cudf::table_view const& right,
cudf::device_span<size_type const> left_indices,
cudf::device_span<size_type const> right_indices,
ast::expression const& predicate,
join_kind join_kind,
rmm::cuda_stream_view stream)
{
// Validate inputs (same constraints as filter_join_indices)
CUDF_EXPECTS(left_indices.size() == right_indices.size(),
"Left and right index arrays must have the same size",
std::invalid_argument);
CUDF_EXPECTS(
join_kind == join_kind::INNER_JOIN || join_kind == join_kind::LEFT_JOIN ||
join_kind == join_kind::FULL_JOIN,
"filter_join_indices_output_size only supports INNER_JOIN, LEFT_JOIN, and FULL_JOIN.",
std::invalid_argument);

if (left_indices.empty()) { return 0; }
if (join_kind == join_kind::LEFT_JOIN && left.num_rows() == 0) { return 0; }

auto const has_nulls = predicate.may_evaluate_null(left, right, stream);

auto const parser = ast::detail::expression_parser{
predicate, left, right, has_nulls, stream, cudf::get_current_device_resource_ref()};

CUDF_EXPECTS(parser.output_type().id() == type_id::BOOL8,
"The predicate expression must produce a Boolean output",
std::invalid_argument);

auto const has_complex_type = parser.has_complex_type();

auto left_table = table_device_view::create(left, stream);
auto right_table = table_device_view::create(right, stream);

detail::grid_1d const config(left_indices.size(), DEFAULT_JOIN_BLOCK_SIZE);
auto const shmem_per_block = parser.shmem_per_thread * DEFAULT_JOIN_BLOCK_SIZE;

Comment thread
PointKernel marked this conversation as resolved.
// The count kernel uses a single atomic counter. Allocate device_scalar zero-initialized.
cudf::detail::device_scalar<std::size_t> d_count(
std::size_t{0}, stream, cudf::get_current_device_resource_ref());

// For LEFT_JOIN, allocate a zeroed per-left-row mark buffer; for others, pass nullptr.
auto left_passing_marks = cudf::detail::make_zeroed_device_uvector_async<bool>(
join_kind == join_kind::LEFT_JOIN ? static_cast<std::size_t>(left.num_rows()) : 0,
stream,
cudf::get_current_device_resource_ref());
auto* const marks_ptr = join_kind == join_kind::LEFT_JOIN ? left_passing_marks.data() : nullptr;

cudf::detail::dispatch_bool(has_nulls, [&](auto has_nulls_c) {
cudf::detail::dispatch_bool(has_complex_type, [&](auto has_complex_c) {
launch_filter_output_size_kernel<decltype(has_nulls_c)::value,
decltype(has_complex_c)::value>(
*left_table,
*right_table,
left_indices,
right_indices,
parser.device_expression_data,
config,
shmem_per_block,
join_kind,
d_count.data(),
marks_ptr,
stream);
});
});

auto const num_predicate_passing = d_count.value(stream);

switch (join_kind) {
case join_kind::INNER_JOIN: return num_predicate_passing;
case join_kind::FULL_JOIN: return left_indices.size() + num_predicate_passing;
case join_kind::LEFT_JOIN: {
auto const num_filter_passing = cudf::detail::count_if(
left_passing_marks.begin(), left_passing_marks.end(), cuda::std::identity{}, stream);
auto const num_invalid = static_cast<std::size_t>(left.num_rows()) - num_filter_passing;
return num_predicate_passing + num_invalid;
}
default: CUDF_FAIL("Unsupported join kind for filter_join_indices_output_size");
}
}

} // namespace detail

// Public API implementation
Expand All @@ -374,4 +462,17 @@ filter_join_indices(cudf::table_view const& left,
left, right, left_indices, right_indices, predicate, join_kind, stream, mr);
}

std::size_t filter_join_indices_output_size(cudf::table_view const& left,
cudf::table_view const& right,
cudf::device_span<size_type const> left_indices,
cudf::device_span<size_type const> right_indices,
ast::expression const& predicate,
cudf::join_kind join_kind,
rmm::cuda_stream_view stream)
{
CUDF_FUNC_RANGE();
return detail::filter_join_indices_output_size(
left, right, left_indices, right_indices, predicate, join_kind, stream);
}

} // namespace cudf
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include "filter_join_indices_jit_kernel.cuh"
#include "jit/filter_join_kernel.cuh"
#include "join/filter_join_indices/filter_join_indices_jit_kernel.cuh"
#include "join/jit/filter_join_kernel.cuh"

#include <cudf/column/column_device_view.cuh>
#include <cudf/detail/algorithms/copy_if.cuh>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

#include "filter_join_indices_kernel.hpp"
#include "join/filter_join_indices/filter_join_indices_kernel.hpp"

#include <cudf/ast/detail/expression_evaluator.cuh>
#include <cudf/ast/detail/expression_parser.hpp>
Expand Down Expand Up @@ -102,6 +102,7 @@ void launch_filter_gather_map_kernel(
right_indices,
device_expression_data,
predicate_results);
CUDF_CUDA_TRY(cudaGetLastError());
}

} // namespace cudf::detail
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

#include "filter_join_indices_kernel.cuh"
#include "filter_join_indices_kernel.hpp"
#include "join/filter_join_indices/filter_join_indices_kernel.cuh"
#include "join/filter_join_indices/filter_join_indices_kernel.hpp"

namespace cudf::detail {
template void launch_filter_gather_map_kernel<false, true>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

#include "filter_join_indices_kernel.cuh"
#include "filter_join_indices_kernel.hpp"
#include "join/filter_join_indices/filter_join_indices_kernel.cuh"
#include "join/filter_join_indices/filter_join_indices_kernel.hpp"

namespace cudf::detail {
template void launch_filter_gather_map_kernel<true, true>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

#include "filter_join_indices_kernel.cuh"
#include "filter_join_indices_kernel.hpp"
#include "join/filter_join_indices/filter_join_indices_kernel.cuh"
#include "join/filter_join_indices/filter_join_indices_kernel.hpp"

namespace cudf::detail {
template void launch_filter_gather_map_kernel<true, false>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

#include "filter_join_indices_kernel.cuh"
#include "filter_join_indices_kernel.hpp"
#include "join/filter_join_indices/filter_join_indices_kernel.cuh"
#include "join/filter_join_indices/filter_join_indices_kernel.hpp"

namespace cudf::detail {
template void launch_filter_gather_map_kernel<false, false>(
Expand Down
Loading
Loading