Skip to content

Parquet Variant Extract Benchmark - #23616

Open
abigalekim wants to merge 9 commits into
NVIDIA:mainfrom
abigalekim:ak/variant-bench
Open

Parquet Variant Extract Benchmark#23616
abigalekim wants to merge 9 commits into
NVIDIA:mainfrom
abigalekim:ak/variant-bench

Conversation

@abigalekim

@abigalekim abigalekim commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds a microbenchmark (VARIANT_NVBENCH) exercising cudf::io::parquet::experimental::extract_variant_field and cast_variant. It contains 3 different benchmarks, which are described below:

  • bench_variant_cast: runs cast_variant (only leaf primitives, no path traversal), with axes num_rows (32k, 256k, 2m rows), type (int32_t / float / bool / string), hit rate (20,80)
  • bench_variant_extract_nesting: evaluates extract_variant_field with varying path depth, with axes num_rows(32k, 256k, 2m rows), type (int32_t / float / bool / string / array), nesting (1 / 5), hit rate (20,80)
    bench_variant_extract_fields: evaluates extract_variant_field on a flat object with varying field count and target position, with axes num_rows(32k, 256k, 2m rows), num_fields (1, 10, 100), field position (first or last), hit rate (20,80)

This solves one of the issues in story issue #22312.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@abigalekim abigalekim added the feature request New feature or request label Aug 11, 2026
@abigalekim
abigalekim requested review from a team as code owners August 11, 2026 00:35
@abigalekim abigalekim added the non-breaking Non-breaking change label Aug 11, 2026
@abigalekim
abigalekim requested review from bdice and simoneves August 11, 2026 00:35
@copy-pr-bot

copy-pr-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@abigalekim
abigalekim marked this pull request as draft August 11, 2026 00:35
@github-actions github-actions Bot added libcudf Affects libcudf (C++/CUDA) code. CMake CMake build issue labels Aug 11, 2026
@abigalekim
abigalekim marked this pull request as ready for review August 11, 2026 01:07
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 20fb1d6d-a4a2-410c-86b2-67da770e7796

📥 Commits

Reviewing files that changed from the base of the PR and between c6c1346 and 81889d1.

📒 Files selected for processing (1)
  • cpp/tests/io/experimental/variant_extract_test.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp/tests/io/experimental/variant_extract_test.cpp

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added CUDA benchmarks for extracting and converting experimental Parquet VARIANT data.
    • Covers primitive, string, Boolean, and array values across configurable row counts, nesting, data types, and match rates.
    • Supports nested paths, array elements, and flat-object field lookup.
    • Includes comparisons between direct VARIANT conversion and path-based extraction.
  • Bug Fixes

    • Improved support for larger VARIANT dictionaries using extended metadata offsets.
    • Added boundary coverage for dictionary sizes around the 255/256-byte threshold.

Walkthrough

This change adds a CUDA NVBench target for Parquet VARIANT casting and field extraction. It generates configurable VARIANT inputs and adds tests for one-byte and two-byte metadata dictionary offsets, including the 255/256-byte boundary.

Changes

VARIANT benchmark

Layer / File(s) Summary
VARIANT encoding and column construction
cpp/benchmarks/io/parquet/experimental/variant/extract.cpp
Encodes metadata and values, generates hit and miss rows, and constructs device-backed STRUCT columns.
Extraction paths and benchmark execution
cpp/benchmarks/io/parquet/experimental/variant/extract.cpp, cpp/benchmarks/CMakeLists.txt
Builds dictionaries, paths, and target types. Runs cast, nested extraction, and flat-object extraction benchmarks. Registers the VARIANT_NVBENCH target.
Metadata offset encoding and validation
cpp/tests/io/experimental/variant_extract_test.cpp
Supports one-byte and two-byte dictionary offsets. Tests 100-key dictionaries and the 255/256-byte boundary.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Mergeability Score: ⚪ Minimal · up to 81889

This PR adds localized Parquet Variant microbenchmarks and benchmark registration; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested labels: improvement

Suggested reviewers: simoneves, bdice, vuule

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: a Parquet VARIANT extraction benchmark.
Description check ✅ Passed The description accurately explains the benchmark targets, coverage, issue addressed, and testing checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/benchmarks/io/parquet/experimental/variant/extract.cpp`:
- Around line 19-22: Add the direct declaration headers <cstdint>,
<cuda_runtime_api.h>, and <tuple> to the includes in extract.cpp for its
fixed-width integer types, cudaStreamSynchronize, and std::ignore usage.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c1a8c46b-67f1-4a01-bbe5-91ef1b75588e

📥 Commits

Reviewing files that changed from the base of the PR and between 98832f4 and 1531d85.

📒 Files selected for processing (2)
  • cpp/benchmarks/CMakeLists.txt
  • cpp/benchmarks/io/parquet/experimental/variant/extract.cpp

Comment thread cpp/benchmarks/io/parquet/experimental/variant/extract.cpp
@abigalekim
abigalekim marked this pull request as draft August 11, 2026 01:14
@abigalekim
abigalekim marked this pull request as ready for review August 12, 2026 20:15
@abigalekim
abigalekim requested review from a team as code owners August 12, 2026 20:15

@vuule vuule left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Primarily a request to expand the coverage, as , for example, this benchmark would not capture the performance improvement from the binary searches.

Comment on lines +238 to +240
for (cudf::size_type i = 0; i < num_rows; ++i) {
val_rows[i] = (static_cast<int>(i % 100) < hit_rate) ? hit_val : miss_val;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would prefer this to be a random chance instead of contiguous ranges of rows with the same hit/miss value.

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.

Yes, I think the function fill_val_rows is now randomized.

});
}

NVBENCH_BENCH(bench_variant_extract)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we also vary the number of fields? With that, we can vary which field we extract (first/last).
With this knob, we have too many parameters to vary in a single benchmark, so we should split into multiple benchmarks.
See benchmarks in #22434, they were useful for the initial implementation.

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 have refactored this code to include a new benchmark that will showcase the benefits of the binary search optimizations.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cpp/benchmarks/io/parquet/experimental/variant/extract.cpp (1)

36-49: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a wider dictionary-offset encoding for the 100-field case.

build_metadata stores cumulative key offsets in uint8_t. The num_fields=100 case creates 101 dictionary keys and 300 bytes of f00 through f99 key data. running wraps after 255 bytes, so the metadata offsets are invalid.

Encode offsets with a VARIANT-supported width that holds the full dictionary, or reduce the field-count axis. Add a regression test that extracts f99 from the 100-field input. As per coding guidelines, **/* requires: “Add unit tests and unit benchmarks.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/benchmarks/io/parquet/experimental/variant/extract.cpp` around lines 36 -
49, Update build_metadata to encode cumulative dictionary offsets using a
VARIANT-supported width that can represent the complete 100-field dictionary
without wrapping, and adjust the metadata layout consistently. Add regression
coverage for extracting f99 from the 100-field input, including the required
unit test and benchmark coverage.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cpp/benchmarks/io/parquet/experimental/variant/extract.cpp`:
- Around line 36-49: Update build_metadata to encode cumulative dictionary
offsets using a VARIANT-supported width that can represent the complete
100-field dictionary without wrapping, and adjust the metadata layout
consistently. Add regression coverage for extracting f99 from the 100-field
input, including the required unit test and benchmark coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 883d4e53-3dd1-4711-800c-a5b568e203c5

📥 Commits

Reviewing files that changed from the base of the PR and between 7e4e725 and 8e864f2.

📒 Files selected for processing (1)
  • cpp/benchmarks/io/parquet/experimental/variant/extract.cpp

@abigalekim
abigalekim requested a review from vuule August 12, 2026 21:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/tests/io/experimental/variant_extract_test.cpp`:
- Around line 783-820: Add boundary-size tests alongside
LargeDictionary100FieldsExtractLast that construct dictionary metadata totaling
exactly 255 bytes and exactly 256 bytes, then extract a representative field and
verify the expected value. Ensure the cases exercise both sides of the
build_metadata total > 255u threshold and preserve the existing coverage for
wider offsets.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1d01bcbf-dd66-4c57-a4df-180fd3204679

📥 Commits

Reviewing files that changed from the base of the PR and between 8e864f2 and c6c1346.

📒 Files selected for processing (2)
  • cpp/benchmarks/io/parquet/experimental/variant/extract.cpp
  • cpp/tests/io/experimental/variant_extract_test.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp/benchmarks/io/parquet/experimental/variant/extract.cpp

Comment thread cpp/tests/io/experimental/variant_extract_test.cpp
std::vector<uint8_t> build_leaf_value(std::string const& type_str)
{
if (type_str == "int32_t") {
std::vector<uint8_t> out{0x14};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I thought we defined an enum somewhere in the tests to reduce the magic numbers. Does that need to be redefined here? There are a lot of magic values (some with comments) floating around. I would rather have named values like we settled on in the tests, iirc.

Comment on lines +154 to +155
std::vector<std::vector<uint8_t>> const& meta_rows,
std::vector<std::vector<uint8_t>> const& val_rows,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we take these as a span-of-spans? Like

Suggested change
std::vector<std::vector<uint8_t>> const& meta_rows,
std::vector<std::vector<uint8_t>> const& val_rows,
std::span<std::span<uint8_t const>> meta_rows,
std::span<std::span<uint8_t const>> val_rows,

There are a few other places where we can do this, too.


cudf::data_type get_target_type(std::string const& type_str)
{
if (type_str == "float") return cudf::data_type{cudf::type_id::FLOAT32};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's use a switch with a default case that throws.

std::vector<uint8_t> offs{0x00};
uint8_t running = 0;
int const offset_size = (total > 255u) ? 2 : 1;
std::vector<uint8_t> out{static_cast<uint8_t>(0x01 | ((offset_size - 1) << 6))};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have no idea what this line means. None of the names or numbers are descriptive. 😕

constexpr uint8_t bool_true_byte = 0x04;

std::vector<uint8_t> val{make_variant_object_header(), static_cast<uint8_t>(n_fields)};
for (int i = 0; i < n_fields; ++i)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We always use braces { ... } even for one-line bodies, for readability and to ensure correctness if the body changes in the future. I'm surprised clang-format let you do this. Maybe we're missing a setting, or excluding test code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CMake CMake build issue feature request New feature or request libcudf Affects libcudf (C++/CUDA) code. non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants