-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Parquet utilities to fetch footer and page index buffers from multiple sources #22613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
41d27df
dbbdea2
5a95c7e
410d495
96e1778
7ae60be
0008932
f552803
da4c404
7b000c5
f42c3e8
50b8bcd
7544248
1ddce7b
94df470
27e01c4
91b281b
4ebce24
f380560
311de40
b92ff65
3767c75
bf5c5be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| #include "io/comp/common.hpp" | ||
| #include "io/parquet/parquet_common.hpp" | ||
|
|
||
| #include <cudf/detail/nvtx/ranges.hpp> | ||
| #include <cudf/detail/utilities/cuda_memcpy.hpp> | ||
| #include <cudf/detail/utilities/host_worker_pool.hpp> | ||
| #include <cudf/detail/utilities/integer_utils.hpp> | ||
|
|
@@ -21,8 +22,12 @@ | |
| #include <cuda/iterator> | ||
| #include <cuda/std/tuple> | ||
|
|
||
| #include <functional> | ||
| #include <mutex> | ||
|
mhaseeb123 marked this conversation as resolved.
|
||
| #include <numeric> | ||
| #include <stdexcept> | ||
| #include <tuple> | ||
| #include <type_traits> | ||
|
|
||
| /** | ||
| * @file parquet_io_utils.cpp | ||
|
|
@@ -31,27 +36,149 @@ | |
|
|
||
| namespace cudf::io::parquet { | ||
|
|
||
| std::unique_ptr<cudf::io::datasource::buffer> fetch_footer_to_host(cudf::io::datasource& datasource) | ||
| namespace detail { | ||
|
|
||
| /** | ||
| * @brief Dispatches the fetch task for each source index and collects the results | ||
| * | ||
| * Dispatches sequentially or using host worker pool depending on the number of sources. | ||
| * | ||
| * @tparam Task Callable invocable as `fetch_task(std::size_t source_idx)` | ||
| * @param num_sources Number of sources to process | ||
| * @param fetch_task Task to run for each source index | ||
| * @return Vector of results, one per source, in source order | ||
| */ | ||
| template <typename Task> | ||
| auto dispatch_fetch_tasks(std::size_t num_sources, Task fetch_task) | ||
|
qbacpey marked this conversation as resolved.
|
||
| { | ||
| constexpr auto header_len = sizeof(file_header_s); | ||
| constexpr auto ender_len = sizeof(file_ender_s); | ||
| size_t const len = datasource.size(); | ||
| using result_type = std::invoke_result_t<Task, std::size_t>; | ||
|
|
||
| auto constexpr parallel_threshold = 32; | ||
|
|
||
| std::vector<result_type> results; | ||
| results.reserve(num_sources); | ||
|
|
||
| if (num_sources < parallel_threshold) { | ||
| // Run sequentially to avoid task dispatch overhead | ||
| std::for_each(cuda::counting_iterator<std::size_t>(0), | ||
| cuda::counting_iterator<std::size_t>(num_sources), | ||
| [&](std::size_t source_idx) { results.emplace_back(fetch_task(source_idx)); }); | ||
| } else { | ||
| // Dispatch the tasks to the host worker pool | ||
| std::vector<std::future<result_type>> tasks; | ||
| tasks.reserve(num_sources); | ||
| std::for_each(cuda::counting_iterator<std::size_t>(0), | ||
| cuda::counting_iterator<std::size_t>(num_sources), | ||
| [&](std::size_t source_idx) { | ||
| tasks.emplace_back(cudf::detail::host_worker_pool().submit_task( | ||
| [&fetch_task, source_idx]() { return fetch_task(source_idx); })); | ||
| }); | ||
| std::transform(tasks.begin(), tasks.end(), std::back_inserter(results), [](auto& task) { | ||
| return task.get(); | ||
| }); | ||
| } | ||
| return results; | ||
| } | ||
|
|
||
| CUDF_EXPECTS(len > header_len + ender_len, "Incorrect data source"); | ||
| /** | ||
| * @copydoc cudf::io::parquet::fetch_footers_to_host | ||
| */ | ||
| std::vector<std::unique_ptr<cudf::io::datasource::buffer>> fetch_footers_to_host( | ||
| cudf::host_span<std::reference_wrapper<cudf::io::datasource> const> datasources) | ||
| { | ||
| // Helper to fetch footer from a datasource | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Helper (fetches one footer) dispatched for all sources in a loop or via thread pool if more than parallel_threshold |
||
| auto const fetch_footer = [](cudf::io::datasource& datasource) { | ||
| constexpr auto header_len = sizeof(file_header_s); | ||
| constexpr auto ender_len = sizeof(file_ender_s); | ||
| size_t const len = datasource.size(); | ||
| CUDF_EXPECTS(len > header_len + ender_len, "Incorrect data source"); | ||
|
|
||
| auto header_buffer = datasource.host_read(0, header_len); | ||
| auto const header = reinterpret_cast<file_header_s const*>(header_buffer->data()); | ||
| auto ender_buffer = datasource.host_read(len - ender_len, ender_len); | ||
| auto const ender = reinterpret_cast<file_ender_s const*>(ender_buffer->data()); | ||
| CUDF_EXPECTS(header->magic == parquet_magic, "Corrupted header"); | ||
| CUDF_EXPECTS(ender->magic == parquet_magic, "Corrupted footer"); | ||
| CUDF_EXPECTS(ender->footer_len != 0 && ender->footer_len <= (len - header_len - ender_len), | ||
| "Incorrect footer length"); | ||
|
|
||
| return datasource.host_read(len - ender->footer_len - ender_len, ender->footer_len); | ||
| }; | ||
|
|
||
| auto ender_buffer = datasource.host_read(len - ender_len, ender_len); | ||
| auto const ender = reinterpret_cast<file_ender_s const*>(ender_buffer->data()); | ||
| CUDF_EXPECTS(ender->magic == detail::parquet_magic, "Corrupted footer"); | ||
| CUDF_EXPECTS(ender->footer_len != 0 && ender->footer_len <= (len - header_len - ender_len), | ||
| "Incorrect footer length"); | ||
| return dispatch_fetch_tasks(datasources.size(), [&](std::size_t source_idx) { | ||
| return fetch_footer(datasources[source_idx].get()); | ||
| }); | ||
| } | ||
|
|
||
| return datasource.host_read(len - ender->footer_len - ender_len, ender->footer_len); | ||
| /** | ||
| * @copydoc cudf::io::parquet::fetch_page_indexes_to_host | ||
| */ | ||
| std::vector<std::unique_ptr<cudf::io::datasource::buffer>> fetch_page_indexes_to_host( | ||
| cudf::host_span<std::reference_wrapper<cudf::io::datasource> const> datasources, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to start complying with the recommendations in #22588?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not yet. Waiting for this reader to be feature complete (few PRs related to this) before I pull the trigger on the entire thing. |
||
| cudf::host_span<cudf::io::text::byte_range_info const> page_index_bytes_per_source) | ||
| { | ||
| CUDF_EXPECTS(datasources.size() == page_index_bytes_per_source.size(), | ||
| "Encountered mismatch in number of datasources and page index byte ranges"); | ||
|
|
||
| // Helper to fetch page index bytes from a datasource | ||
| auto const fetch_page_index = [](cudf::io::datasource& datasource, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Helper (fetches one page index bytes) dispatched for all sources in a loop or via thread pool if more than parallel_threshold |
||
| cudf::io::text::byte_range_info const& page_index_bytes) { | ||
| CUDF_EXPECTS( | ||
| page_index_bytes.offset() >= 0 and | ||
| std::cmp_less_equal(page_index_bytes.offset() + page_index_bytes.size(), datasource.size()), | ||
| std::format("Invalid page index byte range: offset={}, size={}, datasource_size={}", | ||
| page_index_bytes.offset(), | ||
| page_index_bytes.size(), | ||
| datasource.size()), | ||
| std::out_of_range); | ||
| return datasource.host_read(page_index_bytes.offset(), page_index_bytes.size()); | ||
| }; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| return dispatch_fetch_tasks(datasources.size(), [&](std::size_t source_idx) { | ||
| return fetch_page_index(datasources[source_idx].get(), page_index_bytes_per_source[source_idx]); | ||
| }); | ||
| } | ||
|
|
||
| } // namespace detail | ||
|
|
||
| std::unique_ptr<cudf::io::datasource::buffer> fetch_footer_to_host(cudf::io::datasource& datasource) | ||
| { | ||
| CUDF_FUNC_RANGE(); | ||
|
|
||
| // Wrap the input into an array and delegate to the detail multi-source API | ||
| std::array<std::reference_wrapper<cudf::io::datasource>, 1> datasources{std::ref(datasource)}; | ||
| auto footer_buffers = detail::fetch_footers_to_host({datasources.data(), datasources.size()}); | ||
| return std::move(footer_buffers.front()); | ||
| } | ||
|
|
||
| std::vector<std::unique_ptr<cudf::io::datasource::buffer>> fetch_footers_to_host( | ||
| cudf::host_span<std::reference_wrapper<cudf::io::datasource> const> datasources) | ||
| { | ||
| CUDF_FUNC_RANGE(); | ||
| return detail::fetch_footers_to_host(datasources); | ||
| } | ||
|
|
||
| std::unique_ptr<cudf::io::datasource::buffer> fetch_page_index_to_host( | ||
| cudf::io::datasource& datasource, cudf::io::text::byte_range_info const page_index_bytes) | ||
| { | ||
| return datasource.host_read(page_index_bytes.offset(), page_index_bytes.size()); | ||
| CUDF_FUNC_RANGE(); | ||
|
|
||
| // Wrap the inputs into arrays and delegate to the detail multi-source API | ||
| std::array<std::reference_wrapper<cudf::io::datasource>, 1> datasources{std::ref(datasource)}; | ||
| std::array<cudf::io::text::byte_range_info, 1> page_index_bytes_per_source{page_index_bytes}; | ||
|
|
||
| auto page_index_buffers = detail::fetch_page_indexes_to_host( | ||
| {datasources.data(), datasources.size()}, | ||
| {page_index_bytes_per_source.data(), page_index_bytes_per_source.size()}); | ||
| return std::move(page_index_buffers.front()); | ||
| } | ||
|
|
||
| std::vector<std::unique_ptr<cudf::io::datasource::buffer>> fetch_page_indexes_to_host( | ||
| cudf::host_span<std::reference_wrapper<cudf::io::datasource> const> datasources, | ||
| cudf::host_span<cudf::io::text::byte_range_info const> page_index_bytes_per_source) | ||
| { | ||
| CUDF_FUNC_RANGE(); | ||
| return detail::fetch_page_indexes_to_host(datasources, page_index_bytes_per_source); | ||
| } | ||
|
|
||
| std::tuple<std::vector<rmm::device_buffer>, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.