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 a1e6103e perf(core): build commit sequence numbers without a per-row 
allocation (#896)
a1e6103e is described below

commit a1e6103e1817cce146a402b418c031fda2c5e617
Author: Y Ethan Guo <[email protected]>
AuthorDate: Fri Oct 30 08:59:30 2026 -0700

    perf(core): build commit sequence numbers without a per-row allocation 
(#896)
    
    `_hoodie_commit_seqno` was built with a `format!` per row into a 
`Vec<String>` before becoming an Arrow array. A `commit_seqnos` helper now 
writes each value straight into a `StringBuilder`, so the column is one buffer 
with no per-row allocation, and both the append path and the copy-on-write 
restamp of updated rows use it. The values, type and nullability are unchanged.
    
    Locally this cut append CPU time by about 0.25 s for 2.5M rows and 0.5 s 
for 10M rows.
---
 crates/core/src/write/append.rs      | 42 +++++++++++++++++++++++++++++-------
 crates/core/src/write/rewrite/cow.rs |  9 ++++----
 2 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/crates/core/src/write/append.rs b/crates/core/src/write/append.rs
index 3d869039..c3831d1b 100644
--- a/crates/core/src/write/append.rs
+++ b/crates/core/src/write/append.rs
@@ -1055,9 +1055,23 @@ fn schemas_equal_by_fields(a: &arrow_schema::Schema, b: 
&arrow_schema::Schema) -
         .all(|(fa, fb)| fa.name() == fb.name() && fa.data_type() == 
fb.data_type())
 }
 
-/// `_hoodie_commit_seqno`, in Java's `HoodieRecord.generateSequenceId` form.
-pub(crate) fn commit_seqno(instant: &str, partition_id: u32, row: usize) -> 
String {
-    format!("{instant}_{partition_id}_{row}")
+/// `_hoodie_commit_seqno` for `rows` rows numbered from `row_id_offset`, in 
Java's
+/// `HoodieRecord.generateSequenceId` form, written straight into the array's 
buffer.
+pub(crate) fn commit_seqnos(
+    instant: &str,
+    partition_id: u32,
+    row_id_offset: usize,
+    rows: usize,
+) -> StringArray {
+    use std::fmt::Write;
+    let prefix = format!("{instant}_{partition_id}_");
+    let mut builder = arrow::array::StringBuilder::with_capacity(rows, rows * 
(prefix.len() + 10));
+    for row in row_id_offset..row_id_offset + rows {
+        // The builder's `fmt::Write` appends to its value buffer and never 
fails.
+        let _ = write!(builder, "{prefix}{row}");
+        builder.append_value("");
+    }
+    builder.finish()
 }
 
 pub(crate) fn prepare_batches_for_write(
@@ -1114,11 +1128,7 @@ pub(crate) fn prepare_batches_for_write_with_offset(
             fields.extend(batch.schema().fields().iter().cloned());
             let mut columns: Vec<ArrayRef> = vec![
                 Arc::new(StringArray::from(vec![instant; rows])),
-                Arc::new(StringArray::from(
-                    (0..rows)
-                        .map(|row| commit_seqno(instant, partition_id, 
row_id_offset + row))
-                        .collect::<Vec<_>>(),
-                )),
+                Arc::new(commit_seqnos(instant, partition_id, row_id_offset, 
rows)),
                 Arc::new(StringArray::from(key_strs)),
                 Arc::new(StringArray::from(partition_strs)),
                 Arc::new(StringArray::from(vec![file_name; rows])),
@@ -1161,6 +1171,22 @@ mod tests {
         Ok(())
     }
 
+    #[test]
+    fn test_commit_seqnos_number_rows_from_the_offset() {
+        use arrow::array::Array;
+        let seqnos = commit_seqnos("20260101000000000", 3, 98, 3);
+        let got: Vec<&str> = seqnos.iter().map(Option::unwrap).collect();
+        assert_eq!(
+            got,
+            [
+                "20260101000000000_3_98",
+                "20260101000000000_3_99",
+                "20260101000000000_3_100",
+            ]
+        );
+        assert_eq!(commit_seqnos("20260101000000000", 0, 0, 0).len(), 0);
+    }
+
     #[test]
     fn test_arrow_schema_to_avro_json_type_matrix() {
         let schema = Schema::new(vec![
diff --git a/crates/core/src/write/rewrite/cow.rs 
b/crates/core/src/write/rewrite/cow.rs
index c3225d54..0ef063fb 100644
--- a/crates/core/src/write/rewrite/cow.rs
+++ b/crates/core/src/write/rewrite/cow.rs
@@ -443,10 +443,11 @@ pub(super) fn restamp_matched_rows(
         return Ok(batch.clone());
     };
     let new_ct: ArrayRef = std::sync::Arc::new(StringArray::from(vec![instant; 
batch.num_rows()]));
-    let new_sq: ArrayRef = std::sync::Arc::new(StringArray::from(
-        (0..batch.num_rows())
-            .map(|row| crate::write::append::commit_seqno(instant, 0, row))
-            .collect::<Vec<_>>(),
+    let new_sq: ArrayRef = 
std::sync::Arc::new(crate::write::append::commit_seqnos(
+        instant,
+        0,
+        0,
+        batch.num_rows(),
     ));
     let mut columns = batch.columns().to_vec();
     columns[ct_idx] = zip(mask, &new_ct, &columns[ct_idx])?;

Reply via email to