-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Improve parquet footer reading performance by speculatively reading footer bytes #22782
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
36abb36
f6e7eca
ab0fdd0
30dc151
79ba978
8aca3e3
c5a5890
f0f2348
00bd441
08b47b8
a8e201b
cd0a5db
c567b10
b9ec4dd
836ce36
864769d
9d3d2c6
1b04e99
89c3f9e
4c1638a
d4eaa8e
2705e4e
f3e2cc2
37537fb
a81ed97
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 | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |||||||||||||||||||
|
|
||||||||||||||||||||
| #include <cudf/detail/nvtx/ranges.hpp> | ||||||||||||||||||||
| #include <cudf/detail/utilities/cuda_memcpy.hpp> | ||||||||||||||||||||
| #include <cudf/detail/utilities/getenv_or.hpp> | ||||||||||||||||||||
| #include <cudf/detail/utilities/host_worker_pool.hpp> | ||||||||||||||||||||
| #include <cudf/detail/utilities/integer_utils.hpp> | ||||||||||||||||||||
| #include <cudf/io/datasource.hpp> | ||||||||||||||||||||
|
|
@@ -22,12 +23,18 @@ | |||||||||||||||||||
| #include <cuda/iterator> | ||||||||||||||||||||
| #include <cuda/std/tuple> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| #include <algorithm> | ||||||||||||||||||||
| #include <array> | ||||||||||||||||||||
| #include <cstring> | ||||||||||||||||||||
| #include <format> | ||||||||||||||||||||
| #include <functional> | ||||||||||||||||||||
| #include <mutex> | ||||||||||||||||||||
| #include <numeric> | ||||||||||||||||||||
| #include <stdexcept> | ||||||||||||||||||||
| #include <string> | ||||||||||||||||||||
| #include <tuple> | ||||||||||||||||||||
| #include <type_traits> | ||||||||||||||||||||
| #include <vector> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * @file parquet_io_utils.cpp | ||||||||||||||||||||
|
|
@@ -86,23 +93,58 @@ auto dispatch_fetch_tasks(std::size_t num_sources, Task fetch_task) | |||||||||||||||||||
| std::vector<std::unique_ptr<cudf::io::datasource::buffer>> fetch_footers_to_host_impl( | ||||||||||||||||||||
| cudf::host_span<std::reference_wrapper<cudf::io::datasource> const> datasources) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| // Look up runtime configuration once, as late as possible. | ||||||||||||||||||||
| auto const metadata_size_hint = cudf::io::parquet::metadata_size_hint(); | ||||||||||||||||||||
| // Helper to fetch footer from a datasource | ||||||||||||||||||||
| auto const fetch_footer = [](cudf::io::datasource& datasource) { | ||||||||||||||||||||
| auto const fetch_footer = [metadata_size_hint](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 == detail::parquet_magic, "Corrupted header"); | ||||||||||||||||||||
| auto const speculative_read_size = | ||||||||||||||||||||
| std::min(len, std::max(metadata_size_hint, static_cast<size_t>(ender_len))); | ||||||||||||||||||||
| auto const speculative_read_offset = len - speculative_read_size; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| auto speculative_buffer = datasource.host_read(speculative_read_offset, speculative_read_size); | ||||||||||||||||||||
| CUDF_EXPECTS(speculative_buffer->size() == speculative_read_size, | ||||||||||||||||||||
| "Failed to read Parquet speculative metadata bytes"); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| auto const ender = reinterpret_cast<file_ender_s const*>( | ||||||||||||||||||||
| speculative_buffer->data() + speculative_buffer->size() - ender_len); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (speculative_read_offset == 0) { | ||||||||||||||||||||
| auto const header = reinterpret_cast<file_header_s const*>(speculative_buffer->data()); | ||||||||||||||||||||
| CUDF_EXPECTS(header->magic == detail::parquet_magic, "Corrupted header"); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| 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 datasource.host_read(len - ender->footer_len - ender_len, ender->footer_len); | ||||||||||||||||||||
| auto const footer_offset = len - ender->footer_len - ender_len; | ||||||||||||||||||||
| if (footer_offset >= speculative_read_offset) { | ||||||||||||||||||||
| // fastpath: the speculative read includes the full footer. | ||||||||||||||||||||
| auto const footer_start_offset = footer_offset - speculative_read_offset; | ||||||||||||||||||||
| CUDF_EXPECTS(footer_start_offset + ender->footer_len <= speculative_buffer->size(), | ||||||||||||||||||||
| "Speculative metadata read did not include full footer bytes"); | ||||||||||||||||||||
| std::vector<uint8_t> footer_bytes(ender->footer_len); | ||||||||||||||||||||
| std::memcpy( | ||||||||||||||||||||
| footer_bytes.data(), speculative_buffer->data() + footer_start_offset, ender->footer_len); | ||||||||||||||||||||
| return cudf::io::datasource::buffer::create(std::move(footer_bytes)); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // The speculative read only got part of the footer. Read the missing prefix, then stitch. | ||||||||||||||||||||
| auto const missing_prefix_size = speculative_read_offset - footer_offset; | ||||||||||||||||||||
| auto missing_prefix = datasource.host_read(footer_offset, missing_prefix_size); | ||||||||||||||||||||
| CUDF_EXPECTS(missing_prefix->size() == missing_prefix_size, | ||||||||||||||||||||
| "Failed to read the missing footer prefix bytes"); | ||||||||||||||||||||
| std::vector<uint8_t> footer_bytes(ender->footer_len); | ||||||||||||||||||||
| std::memcpy(footer_bytes.data(), missing_prefix->data(), missing_prefix_size); | ||||||||||||||||||||
|
Comment on lines
+138
to
+143
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. Saves us one
Suggested change
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. Thanks @mhaseeb123. With that change, one of the (new) tests fails: And when running all the tests with It seems to me like datasource.host_read(footer_offset, missing_prefix_size, footer_bytes.data());doesn't get us the full footer. IIUC,
Then we'd have But I could easily be misreading things! |
||||||||||||||||||||
| auto const footer_suffix_size = ender->footer_len - missing_prefix_size; | ||||||||||||||||||||
| std::memcpy( | ||||||||||||||||||||
| footer_bytes.data() + missing_prefix_size, speculative_buffer->data(), footer_suffix_size); | ||||||||||||||||||||
| return cudf::io::datasource::buffer::create(std::move(footer_bytes)); | ||||||||||||||||||||
|
mhaseeb123 marked this conversation as resolved.
|
||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return dispatch_fetch_tasks(datasources.size(), [&](std::size_t source_idx) { | ||||||||||||||||||||
|
|
@@ -335,11 +377,16 @@ fetch_byte_ranges_to_device_async_impl( | |||||||||||||||||||
|
|
||||||||||||||||||||
| } // namespace | ||||||||||||||||||||
|
|
||||||||||||||||||||
| [[nodiscard]] std::size_t metadata_size_hint() | ||||||||||||||||||||
| { | ||||||||||||||||||||
| static constexpr auto default_metadata_size_hint = std::size_t{64} * 1024; | ||||||||||||||||||||
| return cudf::detail::getenv_or<std::size_t>("LIBCUDF_PARQUET_METADATA_SIZE_HINT", | ||||||||||||||||||||
| default_metadata_size_hint); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| 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 multi-source implementation | ||||||||||||||||||||
| std::array<std::reference_wrapper<cudf::io::datasource>, 1> datasources{std::ref(datasource)}; | ||||||||||||||||||||
| auto footer_buffers = fetch_footers_to_host_impl({datasources.data(), datasources.size()}); | ||||||||||||||||||||
| return std::move(footer_buffers.front()); | ||||||||||||||||||||
|
|
||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.