This is an automated email from the ASF dual-hosted git repository.

yihua pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hudi-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 53b1a58d perf(core): slice contiguous append chunks instead of copying 
them (#903)
53b1a58d is described below

commit 53b1a58d96b1e2a02619d042ab37b37d7d88cf64
Author: Y Ethan Guo <[email protected]>
AuthorDate: Fri Oct 30 01:37:50 2026 -0700

    perf(core): slice contiguous append chunks instead of copying them (#903)
    
    `append` gathered every chunk through Arrow's `take` kernel, so it copied 
its whole input once more on the preparation thread. The `max_records` split 
now takes `RecordBatch::slice` of the partition batch, a view with no copy. 
`take_rows` returns a slice too when a batch's rows for a partition form a 
contiguous ascending run (non-partitioned or partition-sorted input); any other 
index list still goes through `take`. Every consumer of the chunk works on a 
sliced batch.
---
 crates/core/src/write/append.rs | 40 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/crates/core/src/write/append.rs b/crates/core/src/write/append.rs
index 7c4100b0..e0df03e0 100644
--- a/crates/core/src/write/append.rs
+++ b/crates/core/src/write/append.rs
@@ -209,8 +209,7 @@ pub async fn append_batches(table: &mut Table, batches: 
&[RecordBatch]) -> Resul
         let mut offset = 0usize;
         while offset < total_rows {
             let end = (offset + max_records).min(total_rows);
-            let indices: Vec<u32> = (offset as u32..end as u32).collect();
-            let chunk = take_rows(&partition_batch, &indices)?;
+            let chunk = partition_batch.slice(offset, end - offset);
             let chunk_row_offset = auto_key_row_offset;
             let chunk_keys = hoodie_keys_for_batch_with_offset(
                 &table.hudi_configs,
@@ -245,7 +244,7 @@ pub async fn append_batches(table: &mut Table, batches: 
&[RecordBatch]) -> Resul
                 file_id,
                 file_name,
                 base_file_path,
-                row_count: indices.len() as i64,
+                row_count: (end - offset) as i64,
                 chunk_keys,
                 prepared,
             });
@@ -364,6 +363,15 @@ pub async fn append_batches(table: &mut Table, batches: 
&[RecordBatch]) -> Resul
 }
 
 fn take_rows(batch: &RecordBatch, indices: &[u32]) -> Result<RecordBatch> {
+    // A batch's rows for one partition form a contiguous run when the input is
+    // non-partitioned or sorted by partition; a view avoids copying it.
+    if let Some(first) = indices.first()
+        && indices
+            .windows(2)
+            .all(|w| w[0].checked_add(1) == Some(w[1]))
+    {
+        return Ok(batch.slice(*first as usize, indices.len()));
+    }
     let indices = UInt32Array::from(indices.to_vec());
     let columns = batch
         .columns()
@@ -1046,6 +1054,32 @@ mod tests {
     use super::*;
     use arrow_schema::{DataType, Field, Schema, TimeUnit};
 
+    #[test]
+    fn test_take_rows_slices_a_contiguous_run_and_gathers_the_rest() -> 
Result<()> {
+        let schema = Arc::new(Schema::new(vec![Field::new("v", 
DataType::Int64, false)]));
+        let batch = RecordBatch::try_new(
+            schema,
+            vec![Arc::new(arrow::array::Int64Array::from_iter_values(0..10))],
+        )?;
+        let values = |b: &RecordBatch| -> Vec<i64> {
+            b.column(0)
+                .as_any()
+                .downcast_ref::<arrow::array::Int64Array>()
+                .unwrap()
+                .values()
+                .to_vec()
+        };
+        let values_ptr = |b: &RecordBatch| 
b.column(0).to_data().buffers()[0].as_ptr();
+        let run = take_rows(&batch, &[3, 4, 5])?;
+        assert_eq!(values(&run), [3, 4, 5]);
+        assert_eq!(values_ptr(&run), values_ptr(&batch).wrapping_add(3 * 8));
+        assert_eq!(values(&take_rows(&batch, &[0])?), [0]);
+        assert_eq!(values(&take_rows(&batch, &[9, 2, 2])?), [9, 2, 2]);
+        assert_eq!(values(&take_rows(&batch, &[1, 3])?), [1, 3]);
+        assert_eq!(take_rows(&batch, &[])?.num_rows(), 0);
+        Ok(())
+    }
+
     #[test]
     fn test_arrow_schema_to_avro_json_type_matrix() {
         let schema = Schema::new(vec![

Reply via email to