Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
64be3f0
Add decimal support to VARIANT casting
vuule Aug 27, 2026
1370071
Address review: int128 and sliced coverage, share cast preamble, benc…
vuule Aug 27, 2026
b93a33f
Trim verbose comments in variant decimal cast
vuule Aug 27, 2026
44c8749
Remove redundant decimal cast tests, extend cast matrix with decimal …
vuule Aug 27, 2026
df1bcbc
Cover the int64 decimal representation in tests and benchmarks
vuule Aug 27, 2026
305e6d7
Use cudf::is_fixed_point directly instead of an alias that narrows no…
vuule Aug 27, 2026
79e292e
Merge remote-tracking branch 'origin/main' into variant-decimal-cast
vuule Aug 29, 2026
554e2a3
Merge remote-tracking branch 'origin/main' into variant-decimal-cast
vuule Sep 1, 2026
8327c7c
Name the expected scale in the decimal cast tests
vuule Sep 1, 2026
405e29d
Give expected_scale the scale_type so expectations pass it through
vuule Sep 1, 2026
c8c1660
Tidy up the decimal cast tests
vuule Sep 1, 2026
3fde395
Divide once when rescaling a decimal, in 64 bits where possible
vuule Sep 1, 2026
03e70e0
Load the unscaled payload only for a known decimal width
vuule Sep 1, 2026
e818b78
Merge branch 'main' into variant-decimal-cast
vuule Sep 1, 2026
b15b724
comment update
vuule Sep 2, 2026
370bd42
constexpr1
vuule Sep 2, 2026
3e5e445
constexpr2
vuule Sep 2, 2026
d53483a
constexpr3
vuule Sep 2, 2026
6f348af
Answer the two decided cases of a decimal scale-up up front
vuule Sep 2, 2026
0766f3f
Merge branch 'variant-decimal-cast' of https://github.com/vuule/cudf …
vuule Sep 2, 2026
ba7a2a2
Fix clang-format spacing after the constexpr suggestions
vuule Sep 2, 2026
3fbdfe4
paranoid overflow protection
vuule Sep 2, 2026
e8e95d2
Merge branch 'main' into variant-decimal-cast
vuule Sep 2, 2026
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
47 changes: 45 additions & 2 deletions cpp/benchmarks/io/parquet/experimental/variant/extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,16 @@ using cudf::io::parquet::experimental::variant_basic_type;
using cudf::io::parquet::experimental::variant_primitive_type;

// The leaf value type exercised by the benchmark (nvbench "type" string axis).
enum class bench_variant_type : uint8_t { INT32, FLOAT, BOOL, STRING, ARRAY };
enum class bench_variant_type : uint8_t {
INT32,
FLOAT,
BOOL,
STRING,
ARRAY,
DECIMAL32,
DECIMAL64,
DECIMAL128
};

bench_variant_type parse_bench_variant_type(std::string const& type_str)
{
Expand All @@ -44,9 +53,15 @@ bench_variant_type parse_bench_variant_type(std::string const& type_str)
if (type_str == "bool") { return bench_variant_type::BOOL; }
if (type_str == "string") { return bench_variant_type::STRING; }
if (type_str == "array") { return bench_variant_type::ARRAY; }
if (type_str == "decimal32") { return bench_variant_type::DECIMAL32; }
if (type_str == "decimal64") { return bench_variant_type::DECIMAL64; }
if (type_str == "decimal128") { return bench_variant_type::DECIMAL128; }
CUDF_FAIL("Unrecognized benchmark type: " + type_str);
}

// Shared by the encoded leaf values and the target column, so the cast measures decoding only.
constexpr uint8_t bench_decimal_scale = 2;

// Compose a value-metadata header byte from a basic type and its 6-bit value_header.
// See cpp/tests/io/experimental/variant_extract_test.cpp for the header byte layout.
constexpr uint8_t make_variant_header(variant_basic_type basic, uint8_t value_header)
Expand Down Expand Up @@ -168,6 +183,27 @@ std::vector<uint8_t> build_leaf_value(bench_variant_type type)
out.insert(out.end(), s.begin(), s.end());
return out;
}
case bench_variant_type::DECIMAL32: {
std::vector<uint8_t> out{make_variant_primitive_header(variant_primitive_type::DECIMAL4),
bench_decimal_scale};
append_le(out, 1234u, 4);
return out;
}
case bench_variant_type::DECIMAL64: {
std::vector<uint8_t> out{make_variant_primitive_header(variant_primitive_type::DECIMAL8),
bench_decimal_scale};
append_le(out, (static_cast<uint64_t>(1234u) << 32) | 5678u, 8);
return out;
}
case bench_variant_type::DECIMAL128: {
// The value needs more than 64 bits, so the decode is not measured on an all-zero high half.
std::vector<uint8_t> out{make_variant_primitive_header(variant_primitive_type::DECIMAL16),
bench_decimal_scale};
auto const unscaled = (static_cast<__uint128_t>(1234u) << 64) | 5678u;
append_le(out, static_cast<uint64_t>(unscaled), 8);
append_le(out, static_cast<uint64_t>(unscaled >> 64), 8);
return out;
}
case bench_variant_type::ARRAY: {
// VARIANT array of two INT32 values [42, 99]; element [1] is accessed in the benchmark.
// 2 elements, offsets [0, 5, 10], then INT32(42) and INT32(99) (5 bytes each).
Expand Down Expand Up @@ -368,6 +404,12 @@ cudf::data_type get_target_type(bench_variant_type type)
case bench_variant_type::FLOAT: return cudf::data_type{cudf::type_id::FLOAT32};
case bench_variant_type::BOOL: return cudf::data_type{cudf::type_id::BOOL8};
case bench_variant_type::STRING: return cudf::data_type{cudf::type_id::STRING};
case bench_variant_type::DECIMAL32:
return cudf::data_type{cudf::type_id::DECIMAL32, -bench_decimal_scale};
case bench_variant_type::DECIMAL64:
return cudf::data_type{cudf::type_id::DECIMAL64, -bench_decimal_scale};
case bench_variant_type::DECIMAL128:
return cudf::data_type{cudf::type_id::DECIMAL128, -bench_decimal_scale};
// "array": element access yields INT32.
case bench_variant_type::INT32:
case bench_variant_type::ARRAY: return cudf::data_type{cudf::type_id::INT32};
Expand Down Expand Up @@ -435,7 +477,8 @@ static void bench_variant_cast(nvbench::state& state)
NVBENCH_BENCH(bench_variant_cast)
.set_name("bench_variant_cast")
.add_int64_axis("num_rows", {32768, 262144, 2097152})
.add_string_axis("type", {"string", "float", "bool", "int32_t"})
.add_string_axis("type",
{"string", "float", "bool", "int32_t", "decimal32", "decimal64", "decimal128"})
.add_int64_axis("hit_rate", {20, 80});

// Benchmarks get_variant_field with varying path depth (nesting >= 1). Casting is exercised
Expand Down
13 changes: 9 additions & 4 deletions cpp/include/cudf/io/experimental/variant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,13 @@ namespace io::parquet::experimental {
* A null value is produced when the input row is null or the encoded type does not match
* `desired_type`.
*
* For a decimal `desired_type`, every encoded width is accepted and each value is rescaled from its
* own encoded scale to `desired_type.scale()`, truncating toward zero; a value that no longer fits
* produces a null row with `variant_operation_status::OVERFLOW`.
*
* @param values `list<uint8>` column of VARIANT-encoded value bytes
* @param desired_type Target cuDF type (`STRING`, `INT8`/`INT16`/`INT32`/`INT64`,
* `FLOAT32`/`FLOAT64`, or `BOOL8`)
* `FLOAT32`/`FLOAT64`, `BOOL8`, or `DECIMAL32`/`DECIMAL64`/`DECIMAL128`)
* @param status Optional in-out parameter, `variant_operation_status` values, one per row. Must be
* non-nullable, `UINT8`, and have the same row count as `values`. On input, its existing
* values are treated as status from a prior `get_variant_field` call: rows already marked
Expand All @@ -92,8 +96,8 @@ namespace io::parquet::experimental {
*
* @throws std::invalid_argument if `values` is not a `list<uint8>` column; if `desired_type`
* is not one of the supported types (`STRING`, `INT8`/`INT16`/`INT32`/`INT64`,
* `FLOAT32`/`FLOAT64`, or `BOOL8`); or if `status` is provided but is nullable, not
* `UINT8`, or has a different row count than `values`
* `FLOAT32`/`FLOAT64`, `BOOL8`, or `DECIMAL32`/`DECIMAL64`/`DECIMAL128`); or if `status`
* is provided but is nullable, not `UINT8`, or has a different row count than `values`
*/
[[nodiscard]] std::unique_ptr<column> cast_variant(
column_view const& values,
Expand All @@ -111,7 +115,8 @@ namespace io::parquet::experimental {
* @param variant_column Struct column (VARIANT materialization)
* @param path JSONPath-like path string (see `get_variant_field` for syntax)
* @param desired_type Target type: `STRING`, `INT8`/`INT16`/`INT32`/`INT64`,
* `FLOAT32`/`FLOAT64`, or `BOOL8`
* `FLOAT32`/`FLOAT64`, `BOOL8`, or `DECIMAL32`/`DECIMAL64`/`DECIMAL128` (see `cast_variant`
* for decimal rescaling)
Comment thread
vuule marked this conversation as resolved.
Outdated
* @param status Optional. When provided, filled with `variant_operation_status` values, one per
* row. Must be non-nullable, `UINT8`, and have the same row count as
* `variant_column`
Expand Down
Loading
Loading