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
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ add_library(
src/io/parquet/experimental/hybrid_scan_chunking.cu
src/io/parquet/experimental/hybrid_scan_helpers.cpp
src/io/parquet/experimental/hybrid_scan_impl.cpp
src/io/parquet/experimental/hybrid_scan_multifile.cpp
src/io/parquet/experimental/hybrid_scan_preprocess.cu
src/io/parquet/experimental/page_index_filter.cu
src/io/parquet/experimental/page_index_filter_utils.cu
Expand Down
8 changes: 3 additions & 5 deletions cpp/include/cudf/io/experimental/hybrid_scan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
#include <utility>
#include <vector>

namespace CUDF_EXPORT cudf {
namespace io::parquet::experimental::detail {
namespace cudf::io::parquet::experimental::detail {
Comment thread
mhaseeb123 marked this conversation as resolved.
/**
* @brief Internal experimental Parquet reader optimized for highly selective filters, called a
* Hybrid Scan operation.
*/
class hybrid_scan_reader_impl;
} // namespace io::parquet::experimental::detail
} // namespace CUDF_EXPORT cudf
} // namespace cudf::io::parquet::experimental::detail

//! Using `byte_range_info` from cudf::io::text
using cudf::io::text::byte_range_info;
Expand Down Expand Up @@ -344,7 +342,7 @@ class hybrid_scan_reader {
* @param row_group_indices Input row groups indices
* @return Total number of top-level rows in the row groups
*/
[[nodiscard]] size_type total_rows_in_row_groups(
[[nodiscard]] std::size_t total_rows_in_row_groups(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the cudf::size_type limit as we now support chunked reading.

cudf::host_span<size_type const> row_group_indices) const;

/**
Expand Down
133 changes: 133 additions & 0 deletions cpp/include/cudf/io/experimental/hybrid_scan_multifile.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <cudf/io/experimental/hybrid_scan.hpp>
#include <cudf/io/parquet.hpp>
#include <cudf/io/parquet_schema.hpp>
#include <cudf/io/text/byte_range_info.hpp>
#include <cudf/io/types.hpp>
#include <cudf/types.hpp>
#include <cudf/utilities/export.hpp>

#include <rmm/cuda_stream_view.hpp>
#include <rmm/resource_ref.hpp>

#include <memory>
#include <vector>

namespace cudf::io::parquet::experimental::detail {
/**
* @brief Internal experimental Parquet reader optimized for highly selective filters, called a
* Hybrid Scan operation.
*/
class hybrid_scan_reader_impl;
} // namespace cudf::io::parquet::experimental::detail

//! Using `byte_range_info` from cudf::io::text
using cudf::io::text::byte_range_info;

namespace CUDF_EXPORT cudf {
namespace io::parquet::experimental {
/**
* @addtogroup io_readers
* @{
* @file
*/

/**
* @brief Multi-file variant of the experimental Hybrid Scan Parquet reader
*
* Vectorizes `hybrid_scan_reader` APIs to support multiple Parquet sources. Inputs and outputs are
* indexed by source order except for the row mask which is a single BOOL8 column spanning all rows
* from all sources concatenated in source order, then row-group order within a source.
*
* @note Detailed usage documentation will be added once all APIs are in place. This reader will
* eventually move to `hybrid_scan.hpp` and the existing single-file reader (`hybrid_scan_reader`)
* will become its subclass. Only keeping this separate here for now to reduce noise.
*/
class hybrid_scan_multifile {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to eventually remove this file, move this class to hybrid_scan.hpp and make the existing single-file reader (hybrid_scan_reader) a subclass of this one. Only keeping this separate for now to avoid noise.

public:
/**
* @brief Constructor for the multi-file experimental Parquet reader
*
* @param footer_bytes Host span of Parquet file footer byte spans, one per source
* @param options Parquet reader options
*/
explicit hybrid_scan_multifile(cudf::host_span<cudf::host_span<uint8_t const> const> footer_bytes,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mhaseeb123 can you please check #22560 to see if cudf::host_span is the best fit?

@mhaseeb123 mhaseeb123 May 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to use std::span here if it can be implicitly converted to host_span to pass on to the existing impl class. If not, I would lean towards keep using host_spans until hybrid_scan_multifile is complete (#22583) and then replace all occurrences across hybrid_scan** at once.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: We also gotta make sure that cudf::io::datasource::buffer is also implicitly convertible to std::span

parquet_reader_options const& options);

/**
* @brief Constructor for the multi-file experimental Parquet reader
*
* @param parquet_metadata Host span of pre-populated Parquet file metadata, one per source
* @param options Parquet reader options
*/
explicit hybrid_scan_multifile(cudf::host_span<FileMetaData const> parquet_metadata,
parquet_reader_options const& options);

/**
* @brief Destructor for the multi-file experimental Parquet reader
*/
~hybrid_scan_multifile();

/**
* @brief Get parquet metadatas for all sources
*
* @return Vector of parquet metadata, one per source
*/
[[nodiscard]] std::vector<FileMetaData> parquet_metadatas() const;
Comment thread
mhaseeb123 marked this conversation as resolved.

/**
* @brief Get byte ranges of the page index for all sources
*
* @return Vector of page index byte ranges, one per source
*/
[[nodiscard]] std::vector<byte_range_info> page_index_byte_ranges() const;

/**
* @brief Setup the per-source page index within each Parquet file metadata
*
* @param page_index_bytes Host span of Parquet page index buffer bytes, one per source
*/
void setup_page_indexes(
cudf::host_span<cudf::host_span<uint8_t const> const> page_index_bytes) const;

/**
* @brief Get all available per-source row group indices from the parquet files
*
* @param options Parquet reader options
* @return Vector of row group indices, one inner vector per source
*/
[[nodiscard]] std::vector<std::vector<size_type>> all_row_groups(
parquet_reader_options const& options) const;

/**
* @brief Get the total number of top-level rows in the per-source row groups
*
* @param row_group_indices Input per-source row group indices (one inner vector per source)
* @return Total number of top-level rows across all sources
*/
[[nodiscard]] size_type total_rows_in_row_groups(
cudf::host_span<std::vector<size_type> const> row_group_indices) const;

/**
* @brief Resets the current column selection
*
* Resets the current column selection state forcing column re-selection in subsequent filter,
* byte range, setup chunking and materialization APIs. This is useful if the filter expression
* has been cascaded (and-ed) to include new columns.
*/
void reset_column_selection() const;

private:
std::unique_ptr<detail::hybrid_scan_reader_impl> _impl;
};

/** @} */ // end of group

} // namespace io::parquet::experimental
} // namespace CUDF_EXPORT cudf
17 changes: 9 additions & 8 deletions cpp/src/io/parquet/experimental/hybrid_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,34 @@ namespace cudf::io::parquet::experimental {

hybrid_scan_reader::hybrid_scan_reader(cudf::host_span<uint8_t const> footer_bytes,
parquet_reader_options const& options)
: _impl{std::make_unique<detail::hybrid_scan_reader_impl>(footer_bytes, options)}
: _impl{std::make_unique<detail::hybrid_scan_reader_impl>(
std::vector<cudf::host_span<uint8_t const>>{footer_bytes}, options)}
{
}

hybrid_scan_reader::hybrid_scan_reader(FileMetaData const& parquet_metadata,
parquet_reader_options const& options)
: _impl{std::make_unique<detail::hybrid_scan_reader_impl>(parquet_metadata, options)}
: _impl{std::make_unique<detail::hybrid_scan_reader_impl>(
std::vector<FileMetaData>{parquet_metadata}, options)}
{
}

hybrid_scan_reader::~hybrid_scan_reader() = default;
Comment thread
mhaseeb123 marked this conversation as resolved.

[[nodiscard]] text::byte_range_info hybrid_scan_reader::page_index_byte_range() const
{
return _impl->page_index_byte_range();
return _impl->page_index_byte_ranges().front();
}

[[nodiscard]] FileMetaData hybrid_scan_reader::parquet_metadata() const
{
return _impl->parquet_metadata();
return _impl->parquet_metadatas().front();
}

void hybrid_scan_reader::setup_page_index(cudf::host_span<uint8_t const> page_index_bytes) const
{
CUDF_FUNC_RANGE();

return _impl->setup_page_index(page_index_bytes);
return _impl->setup_page_indexes(std::vector<cudf::host_span<uint8_t const>>{page_index_bytes});
}

std::vector<cudf::size_type> hybrid_scan_reader::all_row_groups(
Expand All @@ -53,10 +54,10 @@ std::vector<cudf::size_type> hybrid_scan_reader::all_row_groups(
// If row groups are specified in parquet reader options, return them as is
if (options.get_row_groups().size() == 1) { return options.get_row_groups().front(); }

return _impl->all_row_groups(options);
return _impl->all_row_groups(options).front();
}

size_type hybrid_scan_reader::total_rows_in_row_groups(
std::size_t hybrid_scan_reader::total_rows_in_row_groups(
cudf::host_span<size_type const> row_group_indices) const
{
if (row_group_indices.empty()) { return 0; }
Expand Down
Loading
Loading