From 4bc412210e478484a075cc14a138004bf5c3d344 Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Fri, 20 Jun 2025 15:59:56 +0800 Subject: [PATCH 1/8] Fast path for string view compare --- arrow-array/src/array/byte_view_array.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index e837512ed064..d873b96552e3 100644 --- a/arrow-array/src/array/byte_view_array.rs +++ b/arrow-array/src/array/byte_view_array.rs @@ -481,7 +481,7 @@ impl GenericByteViewArray { /// Compare two [`GenericByteViewArray`] at index `left_idx` and `right_idx` /// - /// Comparing two ByteView types are non-trivial. + /// Comparing two ByteView types is non-trivial. /// It takes a bit of patience to understand why we don't just compare two &[u8] directly. /// /// ByteView types give us the following two advantages, and we need to be careful not to lose them: @@ -499,29 +499,33 @@ impl GenericByteViewArray { /// e.g., if the inlined 4 bytes are different, we can directly return unequal without looking at the full string. /// /// # Order check flow - /// (1) if both string are smaller than 12 bytes, we can directly compare the data inlined to the view. - /// (2) if any of the string is larger than 12 bytes, we need to compare the full string. + /// (1) if both strings do not contain buffers, we don't need to compute the len of the view, + /// we can compare the views directly. The first 4 bytes of the view is the length of the string, and the rest is the inlined data. + /// (2) if any has no empty buffer, both strings are smaller than 12 bytes, we can directly compare the view. + /// (3) if any of the strings is larger than 12 bytes, we need to compare the full string. /// (2.1) if the inlined 4 bytes are different, we can return the result immediately. /// (2.2) o.w., we need to compare the full string. /// /// # Safety - /// The left/right_idx must within range of each array + /// The left/right_idx must be within range of each array pub unsafe fn compare_unchecked( left: &GenericByteViewArray, left_idx: usize, right: &GenericByteViewArray, right_idx: usize, ) -> std::cmp::Ordering { - let l_view = left.views().get_unchecked(left_idx); - let l_len = *l_view as u32; + if left.data_buffers().is_empty() && right.data_buffers().is_empty() { + // If both arrays have no buffers, we can compare the views directly + return left.views.get_unchecked(left_idx).cmp(right.views.get_unchecked(right_idx)); + } + let l_view = left.views().get_unchecked(left_idx); let r_view = right.views().get_unchecked(right_idx); - let r_len = *r_view as u32; + let l_len = *l_view as u32; + let r_len = *r_view as u32; if l_len <= 12 && r_len <= 12 { - let l_data = unsafe { GenericByteViewArray::::inline_value(l_view, l_len as usize) }; - let r_data = unsafe { GenericByteViewArray::::inline_value(r_view, r_len as usize) }; - return l_data.cmp(r_data); + return l_view.cmp(r_view); } // one of the string is larger than 12 bytes, From 8a552a499e2ac8bb3e8df657ed1966f9d31504e6 Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Sat, 21 Jun 2025 23:26:39 +0800 Subject: [PATCH 2/8] fmt --- arrow-array/src/array/byte_view_array.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index b0525f140a20..c77e49291b42 100644 --- a/arrow-array/src/array/byte_view_array.rs +++ b/arrow-array/src/array/byte_view_array.rs @@ -542,13 +542,16 @@ impl GenericByteViewArray { ) -> std::cmp::Ordering { if left.data_buffers().is_empty() && right.data_buffers().is_empty() { // If both arrays have no buffers, we can compare the views directly - return left.views.get_unchecked(left_idx).cmp(right.views.get_unchecked(right_idx)); + return left + .views + .get_unchecked(left_idx) + .cmp(right.views.get_unchecked(right_idx)); } let l_view = left.views().get_unchecked(left_idx); let r_view = right.views().get_unchecked(right_idx); - let l_len = *l_view as u32; - let r_len = *r_view as u32; + let l_len = *l_view as u32; + let r_len = *r_view as u32; if l_len <= 12 && r_len <= 12 { return l_view.cmp(r_view); From 399297fb4c43ad030f2232a15f9b84c6bd3e003a Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Sun, 22 Jun 2025 11:44:03 +0800 Subject: [PATCH 3/8] Address comments --- arrow-array/src/array/byte_view_array.rs | 25 ++++++++-------------- arrow-ord/src/cmp.rs | 27 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/arrow-array/src/array/byte_view_array.rs b/arrow-array/src/array/byte_view_array.rs index c77e49291b42..713e275d186c 100644 --- a/arrow-array/src/array/byte_view_array.rs +++ b/arrow-array/src/array/byte_view_array.rs @@ -507,7 +507,7 @@ impl GenericByteViewArray { /// Compare two [`GenericByteViewArray`] at index `left_idx` and `right_idx` /// - /// Comparing two ByteView types is non-trivial. + /// Comparing two ByteView types are non-trivial. /// It takes a bit of patience to understand why we don't just compare two &[u8] directly. /// /// ByteView types give us the following two advantages, and we need to be careful not to lose them: @@ -525,36 +525,29 @@ impl GenericByteViewArray { /// e.g., if the inlined 4 bytes are different, we can directly return unequal without looking at the full string. /// /// # Order check flow - /// (1) if both strings do not contain buffers, we don't need to compute the len of the view, - /// we can compare the views directly. The first 4 bytes of the view is the length of the string, and the rest is the inlined data. - /// (2) if any has no empty buffer, both strings are smaller than 12 bytes, we can directly compare the view. - /// (3) if any of the strings is larger than 12 bytes, we need to compare the full string. + /// (1) if both string are smaller than 12 bytes, we can directly compare the data inlined to the view. + /// (2) if any of the string is larger than 12 bytes, we need to compare the full string. /// (2.1) if the inlined 4 bytes are different, we can return the result immediately. /// (2.2) o.w., we need to compare the full string. /// /// # Safety - /// The left/right_idx must be within range of each array + /// The left/right_idx must within range of each array pub unsafe fn compare_unchecked( left: &GenericByteViewArray, left_idx: usize, right: &GenericByteViewArray, right_idx: usize, ) -> std::cmp::Ordering { - if left.data_buffers().is_empty() && right.data_buffers().is_empty() { - // If both arrays have no buffers, we can compare the views directly - return left - .views - .get_unchecked(left_idx) - .cmp(right.views.get_unchecked(right_idx)); - } - let l_view = left.views().get_unchecked(left_idx); - let r_view = right.views().get_unchecked(right_idx); let l_len = *l_view as u32; + + let r_view = right.views().get_unchecked(right_idx); let r_len = *r_view as u32; if l_len <= 12 && r_len <= 12 { - return l_view.cmp(r_view); + let l_data = unsafe { GenericByteViewArray::::inline_value(l_view, l_len as usize) }; + let r_data = unsafe { GenericByteViewArray::::inline_value(r_view, r_len as usize) }; + return l_data.cmp(r_data); } // one of the string is larger than 12 bytes, diff --git a/arrow-ord/src/cmp.rs b/arrow-ord/src/cmp.rs index 2727ff996150..9c40d11ebbb9 100644 --- a/arrow-ord/src/cmp.rs +++ b/arrow-ord/src/cmp.rs @@ -579,12 +579,29 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { return false; } + if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { + // Only need to compare the inlined bytes + let l_bytes = unsafe { GenericByteViewArray::::inline_value(&l_view, 12) }; + let r_bytes = unsafe { GenericByteViewArray::::inline_value(&r_view, 12) }; + return l_bytes.cmp(r_bytes).is_eq(); + } + unsafe { GenericByteViewArray::compare_unchecked(l.0, l.1, r.0, r.1).is_eq() } } fn is_lt(l: Self::Item, r: Self::Item) -> bool { // # Safety // The index is within bounds as it is checked in value() + if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { + // Only need to compare the inlined bytes + let l_bytes = unsafe { + GenericByteViewArray::::inline_value(&l.0.views().get_unchecked(l.1), 12) + }; + let r_bytes = unsafe { + GenericByteViewArray::::inline_value(&r.0.views().get_unchecked(r.1), 12) + }; + return l_bytes.cmp(r_bytes).is_lt(); + } unsafe { GenericByteViewArray::compare_unchecked(l.0, l.1, r.0, r.1).is_lt() } } @@ -626,6 +643,16 @@ pub fn compare_byte_view( ) -> std::cmp::Ordering { assert!(left_idx < left.len()); assert!(right_idx < right.len()); + if left.data_buffers().is_empty() && right.data_buffers().is_empty() { + // Only need to compare the inlined bytes + let l_bytes = unsafe { + GenericByteViewArray::::inline_value(&left.views().get_unchecked(left_idx), 12) + }; + let r_bytes = unsafe { + GenericByteViewArray::::inline_value(&right.views().get_unchecked(right_idx), 12) + }; + return l_bytes.cmp(r_bytes); + } unsafe { GenericByteViewArray::compare_unchecked(left, left_idx, right, right_idx) } } From e9b5e44cb406be630436a1fa81d2dd238058130b Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Sun, 22 Jun 2025 11:46:52 +0800 Subject: [PATCH 4/8] clippy --- arrow-ord/src/cmp.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arrow-ord/src/cmp.rs b/arrow-ord/src/cmp.rs index 9c40d11ebbb9..6cb935b8df8b 100644 --- a/arrow-ord/src/cmp.rs +++ b/arrow-ord/src/cmp.rs @@ -581,8 +581,8 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { // Only need to compare the inlined bytes - let l_bytes = unsafe { GenericByteViewArray::::inline_value(&l_view, 12) }; - let r_bytes = unsafe { GenericByteViewArray::::inline_value(&r_view, 12) }; + let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, 12) }; + let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, 12) }; return l_bytes.cmp(r_bytes).is_eq(); } @@ -595,10 +595,10 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { // Only need to compare the inlined bytes let l_bytes = unsafe { - GenericByteViewArray::::inline_value(&l.0.views().get_unchecked(l.1), 12) + GenericByteViewArray::::inline_value(l.0.views().get_unchecked(l.1), 12) }; let r_bytes = unsafe { - GenericByteViewArray::::inline_value(&r.0.views().get_unchecked(r.1), 12) + GenericByteViewArray::::inline_value(r.0.views().get_unchecked(r.1), 12) }; return l_bytes.cmp(r_bytes).is_lt(); } @@ -646,10 +646,10 @@ pub fn compare_byte_view( if left.data_buffers().is_empty() && right.data_buffers().is_empty() { // Only need to compare the inlined bytes let l_bytes = unsafe { - GenericByteViewArray::::inline_value(&left.views().get_unchecked(left_idx), 12) + GenericByteViewArray::::inline_value(left.views().get_unchecked(left_idx), 12) }; let r_bytes = unsafe { - GenericByteViewArray::::inline_value(&right.views().get_unchecked(right_idx), 12) + GenericByteViewArray::::inline_value(right.views().get_unchecked(right_idx), 12) }; return l_bytes.cmp(r_bytes); } From c089841355b9d22c20cab7c86ca609779e2ef4b2 Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Sun, 22 Jun 2025 12:47:46 +0800 Subject: [PATCH 5/8] add benchmark --- arrow-ord/src/cmp.rs | 19 +++++++++---------- arrow/benches/comparison_kernels.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/arrow-ord/src/cmp.rs b/arrow-ord/src/cmp.rs index 6cb935b8df8b..a674fdd80d85 100644 --- a/arrow-ord/src/cmp.rs +++ b/arrow-ord/src/cmp.rs @@ -566,12 +566,18 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { type Item = (&'a GenericByteViewArray, usize); fn is_eq(l: Self::Item, r: Self::Item) -> bool { + let l_view = unsafe { l.0.views().get_unchecked(l.1) }; + let r_view = unsafe { r.0.views().get_unchecked(r.1) }; + if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { + // Only need to compare the inlined bytes + let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, 12) }; + let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, 12) }; + return l_bytes.cmp(r_bytes).is_eq(); + } + // # Safety // The index is within bounds as it is checked in value() - let l_view = unsafe { l.0.views().get_unchecked(l.1) }; let l_len = *l_view as u32; - - let r_view = unsafe { r.0.views().get_unchecked(r.1) }; let r_len = *r_view as u32; // This is a fast path for equality check. // We don't need to look at the actual bytes to determine if they are equal. @@ -579,13 +585,6 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { return false; } - if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { - // Only need to compare the inlined bytes - let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, 12) }; - let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, 12) }; - return l_bytes.cmp(r_bytes).is_eq(); - } - unsafe { GenericByteViewArray::compare_unchecked(l.0, l.1, r.0, r.1).is_eq() } } diff --git a/arrow/benches/comparison_kernels.rs b/arrow/benches/comparison_kernels.rs index c29200d8000a..6a02deb41ad5 100644 --- a/arrow/benches/comparison_kernels.rs +++ b/arrow/benches/comparison_kernels.rs @@ -69,6 +69,17 @@ fn make_string_array(size: usize, rng: &mut StdRng) -> impl Iterator impl Iterator> + '_ { + (0..size).map(|_| { + let len = rng.random_range(0..12); + let bytes = (0..len).map(|_| rng.random_range(0..128)).collect(); + Some(String::from_utf8(bytes).unwrap()) + }) +} + fn add_benchmark(c: &mut Criterion) { let arr_a = create_primitive_array_with_seed::(SIZE, 0.0, 42); let arr_b = create_primitive_array_with_seed::(SIZE, 0.0, 43); @@ -226,6 +237,23 @@ fn add_benchmark(c: &mut Criterion) { b.iter(|| eq(&string_view_left, &string_view_right).unwrap()) }); + let array_gen = make_inlined_string_array(1024 * 1024 * 8, &mut rng); + let string_left = StringArray::from_iter(array_gen); + let string_view_inlined_left = StringViewArray::from_iter(string_left.iter()); + + let array_gen = make_inlined_string_array(1024 * 1024 * 8, &mut rng); + let string_right = StringArray::from_iter(array_gen); + let string_view_inlined_right = StringViewArray::from_iter(string_right.iter()); + + // Add fast path benchmarks for StringViewArray, both side are inlined views < 12 bytes + c.bench_function("eq StringViewArray StringViewArray inlined bytes", |b| { + b.iter(|| eq(&string_view_inlined_left, &string_view_inlined_right).unwrap()) + }); + + c.bench_function("lt StringViewArray StringViewArray inlined bytes", |b| { + b.iter(|| lt(&string_view_inlined_left, &string_view_inlined_right).unwrap()) + }); + // eq benchmarks for long strings with the same prefix c.bench_function("eq long same prefix strings StringArray", |b| { b.iter(|| eq(&left_arr_long_string, &right_arr_long_string).unwrap()) From dddf67a134dbd60e3ea5449da277fa31897b9857 Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Sun, 22 Jun 2025 20:30:07 +0800 Subject: [PATCH 6/8] address comments --- arrow-ord/src/cmp.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arrow-ord/src/cmp.rs b/arrow-ord/src/cmp.rs index a674fdd80d85..70ed30588e05 100644 --- a/arrow-ord/src/cmp.rs +++ b/arrow-ord/src/cmp.rs @@ -565,6 +565,7 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { /// Item.0 is the array, Item.1 is the index type Item = (&'a GenericByteViewArray, usize); + #[inline(always)] fn is_eq(l: Self::Item, r: Self::Item) -> bool { let l_view = unsafe { l.0.views().get_unchecked(l.1) }; let r_view = unsafe { r.0.views().get_unchecked(r.1) }; @@ -575,8 +576,6 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { return l_bytes.cmp(r_bytes).is_eq(); } - // # Safety - // The index is within bounds as it is checked in value() let l_len = *l_view as u32; let r_len = *r_view as u32; // This is a fast path for equality check. @@ -585,12 +584,13 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { return false; } + // # Safety + // The index is within bounds as it is checked in value() unsafe { GenericByteViewArray::compare_unchecked(l.0, l.1, r.0, r.1).is_eq() } } + #[inline(always)] fn is_lt(l: Self::Item, r: Self::Item) -> bool { - // # Safety - // The index is within bounds as it is checked in value() if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { // Only need to compare the inlined bytes let l_bytes = unsafe { @@ -601,6 +601,8 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { }; return l_bytes.cmp(r_bytes).is_lt(); } + // # Safety + // The index is within bounds as it is checked in value() unsafe { GenericByteViewArray::compare_unchecked(l.0, l.1, r.0, r.1).is_lt() } } @@ -634,6 +636,7 @@ impl<'a> ArrayOrd for &'a FixedSizeBinaryArray { } /// Compares two [`GenericByteViewArray`] at index `left_idx` and `right_idx` +#[inline(always)] pub fn compare_byte_view( left: &GenericByteViewArray, left_idx: usize, From 4fa53b5913b1e625f71eb55cf05f2728332c0b72 Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Sun, 22 Jun 2025 22:24:12 +0800 Subject: [PATCH 7/8] new code --- arrow-ord/src/cmp.rs | 45 +++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/arrow-ord/src/cmp.rs b/arrow-ord/src/cmp.rs index 70ed30588e05..d2f0cf377594 100644 --- a/arrow-ord/src/cmp.rs +++ b/arrow-ord/src/cmp.rs @@ -569,13 +569,6 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { fn is_eq(l: Self::Item, r: Self::Item) -> bool { let l_view = unsafe { l.0.views().get_unchecked(l.1) }; let r_view = unsafe { r.0.views().get_unchecked(r.1) }; - if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { - // Only need to compare the inlined bytes - let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, 12) }; - let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, 12) }; - return l_bytes.cmp(r_bytes).is_eq(); - } - let l_len = *l_view as u32; let r_len = *r_view as u32; // This is a fast path for equality check. @@ -584,6 +577,18 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { return false; } + // When both len are same, we can compare the inlined bytes, this can handle the corner case after + // the len compare, for example: + // > println!("{:?}", "\0".cmp("")) + // > Greater + if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { + // If the lengths are equal, we can compare the inlined bytes + // Only need to compare the inlined bytes + let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, 12) }; + let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, 12) }; + return l_bytes.cmp(r_bytes).is_eq(); + } + // # Safety // The index is within bounds as it is checked in value() unsafe { GenericByteViewArray::compare_unchecked(l.0, l.1, r.0, r.1).is_eq() } @@ -592,13 +597,12 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { #[inline(always)] fn is_lt(l: Self::Item, r: Self::Item) -> bool { if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { - // Only need to compare the inlined bytes - let l_bytes = unsafe { - GenericByteViewArray::::inline_value(l.0.views().get_unchecked(l.1), 12) - }; - let r_bytes = unsafe { - GenericByteViewArray::::inline_value(r.0.views().get_unchecked(r.1), 12) - }; + let l_view = unsafe { l.0.views().get_unchecked(l.1) }; + let r_view = unsafe { r.0.views().get_unchecked(r.1) }; + let l_len = *l_view as u32 as usize; + let r_len = *r_view as u32 as usize; + let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, l_len) }; + let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, r_len) }; return l_bytes.cmp(r_bytes).is_lt(); } // # Safety @@ -646,13 +650,12 @@ pub fn compare_byte_view( assert!(left_idx < left.len()); assert!(right_idx < right.len()); if left.data_buffers().is_empty() && right.data_buffers().is_empty() { - // Only need to compare the inlined bytes - let l_bytes = unsafe { - GenericByteViewArray::::inline_value(left.views().get_unchecked(left_idx), 12) - }; - let r_bytes = unsafe { - GenericByteViewArray::::inline_value(right.views().get_unchecked(right_idx), 12) - }; + let l_view = unsafe { left.views().get_unchecked(left_idx) }; + let r_view = unsafe { right.views().get_unchecked(right_idx) }; + let l_len = *l_view as u32 as usize; + let r_len = *r_view as u32 as usize; + let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, l_len) }; + let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, r_len) }; return l_bytes.cmp(r_bytes); } unsafe { GenericByteViewArray::compare_unchecked(left, left_idx, right, right_idx) } From fb82e8bed18270dd29f9d498e7f2b6bf5e8468eb Mon Sep 17 00:00:00 2001 From: zhuqi-lucas <821684824@qq.com> Date: Mon, 23 Jun 2025 07:36:12 +0800 Subject: [PATCH 8/8] address comments --- arrow-ord/src/cmp.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/arrow-ord/src/cmp.rs b/arrow-ord/src/cmp.rs index d2f0cf377594..46cab1bb8e4c 100644 --- a/arrow-ord/src/cmp.rs +++ b/arrow-ord/src/cmp.rs @@ -569,6 +569,11 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { fn is_eq(l: Self::Item, r: Self::Item) -> bool { let l_view = unsafe { l.0.views().get_unchecked(l.1) }; let r_view = unsafe { r.0.views().get_unchecked(r.1) }; + if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { + // For eq case, we can directly compare the inlined bytes + return l_view.cmp(r_view).is_eq(); + } + let l_len = *l_view as u32; let r_len = *r_view as u32; // This is a fast path for equality check. @@ -577,18 +582,6 @@ impl<'a, T: ByteViewType> ArrayOrd for &'a GenericByteViewArray { return false; } - // When both len are same, we can compare the inlined bytes, this can handle the corner case after - // the len compare, for example: - // > println!("{:?}", "\0".cmp("")) - // > Greater - if l.0.data_buffers().is_empty() && r.0.data_buffers().is_empty() { - // If the lengths are equal, we can compare the inlined bytes - // Only need to compare the inlined bytes - let l_bytes = unsafe { GenericByteViewArray::::inline_value(l_view, 12) }; - let r_bytes = unsafe { GenericByteViewArray::::inline_value(r_view, 12) }; - return l_bytes.cmp(r_bytes).is_eq(); - } - // # Safety // The index is within bounds as it is checked in value() unsafe { GenericByteViewArray::compare_unchecked(l.0, l.1, r.0, r.1).is_eq() }