comphead commented on code in PR #7201:
URL: https://github.com/apache/datafusion-comet/pull/7201#discussion_r4417473746
##########
native/shuffle/benches/row_columnar.rs:
##########
@@ -559,6 +839,156 @@ fn benchmark_sorted_spill(c: &mut Criterion) {
group.finish();
}
+/// Map<Utf8, Int64> columns, at the entry counts of `map_conversion` so the
two can be compared.
+///
+/// Keys go through the string path of the array reader: each one is read
through an
+/// offset+size slot relative to the key array and appended to a
`StringBuilder` one at a time,
+/// where Int64 keys are copied out in one slice. Keys are distinct within a
map and vary from
+/// row to row, in length too, and values are distinct per row. Each fixture
is converted and
+/// read back once, and checked against the entries it was built from, before
it is timed.
+fn benchmark_utf8_map_conversion(c: &mut Criterion) {
+ let mut group = c.benchmark_group("map_utf8_conversion");
+
+ for num_entries in [10, 100] {
+ for num_rows in [1000, 10000] {
+ let schema = vec![make_utf8_map_schema()];
+ let entries: Vec<(Vec<String>, Vec<i64>)> = (0..num_rows)
+ .map(|row| {
+ (0..num_entries)
+ .map(|i| (format!("key_{i}_{}", row % 97), (row * 100
+ i) as i64))
+ .unzip()
+ })
+ .collect();
+ let rows: Vec<Vec<u8>> = entries
+ .iter()
+ .map(|(keys, values)| build_utf8_map_row(keys, values))
+ .collect();
+
+ let batch = convert_and_read_back(&schema, &rows, 1.0);
+ let maps = batch.column(0).as_map();
+ assert_eq!(maps.null_count(), 0);
+ for (row, (keys, values)) in entries.iter().enumerate() {
+ let entry = maps.value(row);
+ let actual_keys = entry.column(0).as_string::<i32>();
+ let actual_values =
entry.column(1).as_primitive::<Int64Type>();
+ assert_eq!(entry.len(), num_entries, "row {row}");
+ for (i, (key, value)) in keys.iter().zip(values).enumerate() {
+ assert_eq!(actual_keys.value(i), key, "row {row} key {i}");
+ assert!(actual_values.is_valid(i), "row {row} value {i}");
+ assert_eq!(actual_values.value(i), *value, "row {row}
value {i}");
+ }
+ }
+
+ run_benchmark(
+ &mut group,
+ &format!("entries_{num_entries}"),
+ &format!("rows_{num_rows}"),
+ &schema,
+ &rows,
+ 1,
+ 1.0,
+ );
+ }
+ }
+
+ group.finish();
+}
+
+/// Primitive and map columns holding nulls. Every other case here is free of
nulls, so it never
+/// reads a set null bit or appends a null.
+///
+/// - `cols_100_null_10pct`: the 100 Int64 columns of `primitive_columns`,
with a tenth of the
+/// fields null. Each null is found through the row's null bitset and
appended as a null.
+/// - `map_entries_{10,100}_value_null_10pct`: `Map<Int64, Int64>` with a
tenth of the values
+/// null. The value array is read as nullable, and an array with a null
skips the plain copy
+/// that a null-free array takes. At 10 entries about a third of the maps
hold no null and the
+/// rest go element by element; at 100 entries nearly every map holds one
and takes the copy
+/// with a validity buffer built from the null bitset (64 elements and up,
in `list.rs`).
+///
+/// The nulls fall by a hash of row and position, values vary per row, and
every row is built
+/// separately. Each fixture is converted and read back once, and checked null
for null and value
+/// for value against how it was built, before it is timed.
+fn benchmark_nullable_conversion(c: &mut Criterion) {
+ let mut group = c.benchmark_group("nullable_conversion");
+ const NULL_FRACTION: f64 = 0.1;
+ const NUM_COLS: usize = 100;
+
+ for num_rows in [1000, 10000] {
+ let schema = vec![ArrowDataType::Int64; NUM_COLS];
+ let rows: Vec<Vec<u8>> = (0..num_rows)
+ .map(|row| build_primitive_row(row, NUM_COLS, NULL_FRACTION))
Review Comment:
The nullable cases cannot be compared with the nearest null-free ones to get
the cost of nulls, because the data differs too. `primitive_columns` writes the
same byte pattern into every row, while this case writes distinct values per
row, and `map_conversion` repeats one row while the nullable map rows vary by
`row`. Zstd compression and the file write are inside the timed loop, so the
gap mixes compressing distinct against repeated rows with the null handling.
Since both builders already take the fraction, could each group add a
`null_0pct` control on the same data, `build_primitive_row(row, NUM_COLS, 0.0)`
and `build_map_row(num_entries, row, 0.0)`? I have not run the benchmark.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]