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

apitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new 52321377cc GH-40997: [C++] Get null_bit_id according to 
are_cols_in_encoding_order in NullUpdateColumnToRow_avx2 (#40998)
52321377cc is described below

commit 52321377cc9fbcb8678577f10232aea984a235f5
Author: ZhangHuiGui <[email protected]>
AuthorDate: Tue May 7 05:13:44 2024 -0400

    GH-40997: [C++] Get null_bit_id according to are_cols_in_encoding_order in 
NullUpdateColumnToRow_avx2 (#40998)
    
    
    
    ### Rationale for this change
    Recently, we find that the compare internal's avx2 function 
NullUpdateColumnToRowImp_avx2 lost the are_cols_in_encoding_order check when 
get null_bit_id. It may cause grouper's compare result 
wrong(are_cols_in_encoding_order = true in grouper).
    
    ### What changes are included in this PR?
    Get `null_bit_id` according to `are_cols_in_encoding_order` in 
NullUpdateColumnToRow_avx2.
    
    ### Are there any user-facing changes?
    No
    
    Co-authored-by laotan332 <qinpengxiang@ outlook.com>
    Co-authored-by ZhangHuiGui <2689496754@ qq.com>
    
    * GitHub Issue: #40997
    
    Lead-authored-by: ZhangHuiGui <[email protected]>
    Co-authored-by: ZhangHuiGui <[email protected]>
    Signed-off-by: Antoine Pitrou <[email protected]>
---
 cpp/src/arrow/compute/CMakeLists.txt               |  3 +-
 cpp/src/arrow/compute/row/compare_internal.cc      | 41 ++++++-------
 cpp/src/arrow/compute/row/compare_internal.h       | 25 ++++----
 cpp/src/arrow/compute/row/compare_internal_avx2.cc | 20 ++++---
 cpp/src/arrow/compute/row/grouper_test.cc          | 68 ++++++++++++++++++++++
 cpp/src/arrow/compute/row/row_internal.cc          |  3 +-
 6 files changed, 116 insertions(+), 44 deletions(-)

diff --git a/cpp/src/arrow/compute/CMakeLists.txt 
b/cpp/src/arrow/compute/CMakeLists.txt
index badcf4f2f2..fb778be113 100644
--- a/cpp/src/arrow/compute/CMakeLists.txt
+++ b/cpp/src/arrow/compute/CMakeLists.txt
@@ -90,7 +90,8 @@ add_arrow_test(internals_test
                light_array_test.cc
                registry_test.cc
                key_hash_test.cc
-               row/compare_test.cc)
+               row/compare_test.cc
+               row/grouper_test.cc)
 
 add_arrow_compute_test(expression_test SOURCES expression_test.cc)
 
diff --git a/cpp/src/arrow/compute/row/compare_internal.cc 
b/cpp/src/arrow/compute/row/compare_internal.cc
index 078a8287c7..98aea90112 100644
--- a/cpp/src/arrow/compute/row/compare_internal.cc
+++ b/cpp/src/arrow/compute/row/compare_internal.cc
@@ -36,22 +36,22 @@ void KeyCompare::NullUpdateColumnToRow(uint32_t id_col, 
uint32_t num_rows_to_com
                                        const uint32_t* left_to_right_map,
                                        LightContext* ctx, const 
KeyColumnArray& col,
                                        const RowTableImpl& rows,
-                                       uint8_t* match_bytevector,
-                                       bool are_cols_in_encoding_order) {
+                                       bool are_cols_in_encoding_order,
+                                       uint8_t* match_bytevector) {
   if (!rows.has_any_nulls(ctx) && !col.data(0)) {
     return;
   }
   uint32_t num_processed = 0;
 #if defined(ARROW_HAVE_RUNTIME_AVX2)
   if (ctx->has_avx2()) {
-    num_processed = NullUpdateColumnToRow_avx2(use_selection, id_col, 
num_rows_to_compare,
-                                               sel_left_maybe_null, 
left_to_right_map,
-                                               ctx, col, rows, 
match_bytevector);
+    num_processed = NullUpdateColumnToRow_avx2(
+        use_selection, id_col, num_rows_to_compare, sel_left_maybe_null,
+        left_to_right_map, ctx, col, rows, are_cols_in_encoding_order, 
match_bytevector);
   }
 #endif
 
-  uint32_t null_bit_id =
-      are_cols_in_encoding_order ? id_col : 
rows.metadata().pos_after_encoding(id_col);
+  const uint32_t null_bit_id =
+      ColIdInEncodingOrder(rows, id_col, are_cols_in_encoding_order);
 
   if (!col.data(0)) {
     // Remove rows from the result for which the column value is a null
@@ -363,10 +363,9 @@ void KeyCompare::CompareColumnsToRows(
       continue;
     }
 
-    uint32_t offset_within_row = rows.metadata().encoded_field_offset(
-        are_cols_in_encoding_order
-            ? static_cast<uint32_t>(icol)
-            : rows.metadata().pos_after_encoding(static_cast<uint32_t>(icol)));
+    uint32_t offset_within_row =
+        rows.metadata().encoded_field_offset(ColIdInEncodingOrder(
+            rows, static_cast<uint32_t>(icol), are_cols_in_encoding_order));
     if (col.metadata().is_fixed_length) {
       if (sel_left_maybe_null) {
         CompareBinaryColumnToRow<true>(
@@ -375,9 +374,8 @@ void KeyCompare::CompareColumnsToRows(
             is_first_column ? match_bytevector_A : match_bytevector_B);
         NullUpdateColumnToRow<true>(
             static_cast<uint32_t>(icol), num_rows_to_compare, 
sel_left_maybe_null,
-            left_to_right_map, ctx, col, rows,
-            is_first_column ? match_bytevector_A : match_bytevector_B,
-            are_cols_in_encoding_order);
+            left_to_right_map, ctx, col, rows, are_cols_in_encoding_order,
+            is_first_column ? match_bytevector_A : match_bytevector_B);
       } else {
         // Version without using selection vector
         CompareBinaryColumnToRow<false>(
@@ -386,9 +384,8 @@ void KeyCompare::CompareColumnsToRows(
             is_first_column ? match_bytevector_A : match_bytevector_B);
         NullUpdateColumnToRow<false>(
             static_cast<uint32_t>(icol), num_rows_to_compare, 
sel_left_maybe_null,
-            left_to_right_map, ctx, col, rows,
-            is_first_column ? match_bytevector_A : match_bytevector_B,
-            are_cols_in_encoding_order);
+            left_to_right_map, ctx, col, rows, are_cols_in_encoding_order,
+            is_first_column ? match_bytevector_A : match_bytevector_B);
       }
       if (!is_first_column) {
         AndByteVectors(ctx, num_rows_to_compare, match_bytevector_A, 
match_bytevector_B);
@@ -414,9 +411,8 @@ void KeyCompare::CompareColumnsToRows(
         }
         NullUpdateColumnToRow<true>(
             static_cast<uint32_t>(icol), num_rows_to_compare, 
sel_left_maybe_null,
-            left_to_right_map, ctx, col, rows,
-            is_first_column ? match_bytevector_A : match_bytevector_B,
-            are_cols_in_encoding_order);
+            left_to_right_map, ctx, col, rows, are_cols_in_encoding_order,
+            is_first_column ? match_bytevector_A : match_bytevector_B);
       } else {
         if (ivarbinary == 0) {
           CompareVarBinaryColumnToRow<false, true>(
@@ -429,9 +425,8 @@ void KeyCompare::CompareColumnsToRows(
         }
         NullUpdateColumnToRow<false>(
             static_cast<uint32_t>(icol), num_rows_to_compare, 
sel_left_maybe_null,
-            left_to_right_map, ctx, col, rows,
-            is_first_column ? match_bytevector_A : match_bytevector_B,
-            are_cols_in_encoding_order);
+            left_to_right_map, ctx, col, rows, are_cols_in_encoding_order,
+            is_first_column ? match_bytevector_A : match_bytevector_B);
       }
       if (!is_first_column) {
         AndByteVectors(ctx, num_rows_to_compare, match_bytevector_A, 
match_bytevector_B);
diff --git a/cpp/src/arrow/compute/row/compare_internal.h 
b/cpp/src/arrow/compute/row/compare_internal.h
index b039ca97ff..16002ee518 100644
--- a/cpp/src/arrow/compute/row/compare_internal.h
+++ b/cpp/src/arrow/compute/row/compare_internal.h
@@ -43,13 +43,19 @@ class ARROW_EXPORT KeyCompare {
       uint8_t* out_match_bitvector_maybe_null = NULLPTR);
 
  private:
+  static uint32_t ColIdInEncodingOrder(const RowTableImpl& rows, uint32_t 
id_col,
+                                       bool are_cols_in_encoding_order) {
+    return are_cols_in_encoding_order ? id_col
+                                      : 
rows.metadata().pos_after_encoding(id_col);
+  }
+
   template <bool use_selection>
   static void NullUpdateColumnToRow(uint32_t id_col, uint32_t 
num_rows_to_compare,
                                     const uint16_t* sel_left_maybe_null,
                                     const uint32_t* left_to_right_map, 
LightContext* ctx,
                                     const KeyColumnArray& col, const 
RowTableImpl& rows,
-                                    uint8_t* match_bytevector,
-                                    bool are_cols_in_encoding_order);
+                                    bool are_cols_in_encoding_order,
+                                    uint8_t* match_bytevector);
 
   template <bool use_selection, class COMPARE_FN>
   static void CompareBinaryColumnToRowHelper(
@@ -92,7 +98,8 @@ class ARROW_EXPORT KeyCompare {
   static uint32_t NullUpdateColumnToRowImp_avx2(
       uint32_t id_col, uint32_t num_rows_to_compare, const uint16_t* 
sel_left_maybe_null,
       const uint32_t* left_to_right_map, LightContext* ctx, const 
KeyColumnArray& col,
-      const RowTableImpl& rows, uint8_t* match_bytevector);
+      const RowTableImpl& rows, bool are_cols_in_encoding_order,
+      uint8_t* match_bytevector);
 
   template <bool use_selection, class COMPARE8_FN>
   static uint32_t CompareBinaryColumnToRowHelper_avx2(
@@ -118,13 +125,11 @@ class ARROW_EXPORT KeyCompare {
   static uint32_t AndByteVectors_avx2(uint32_t num_elements, uint8_t* 
bytevector_A,
                                       const uint8_t* bytevector_B);
 
-  static uint32_t NullUpdateColumnToRow_avx2(bool use_selection, uint32_t 
id_col,
-                                             uint32_t num_rows_to_compare,
-                                             const uint16_t* 
sel_left_maybe_null,
-                                             const uint32_t* left_to_right_map,
-                                             LightContext* ctx, const 
KeyColumnArray& col,
-                                             const RowTableImpl& rows,
-                                             uint8_t* match_bytevector);
+  static uint32_t NullUpdateColumnToRow_avx2(
+      bool use_selection, uint32_t id_col, uint32_t num_rows_to_compare,
+      const uint16_t* sel_left_maybe_null, const uint32_t* left_to_right_map,
+      LightContext* ctx, const KeyColumnArray& col, const RowTableImpl& rows,
+      bool are_cols_in_encoding_order, uint8_t* match_bytevector);
 
   static uint32_t CompareBinaryColumnToRow_avx2(
       bool use_selection, uint32_t offset_within_row, uint32_t 
num_rows_to_compare,
diff --git a/cpp/src/arrow/compute/row/compare_internal_avx2.cc 
b/cpp/src/arrow/compute/row/compare_internal_avx2.cc
index ff407c51b8..18f656a2e4 100644
--- a/cpp/src/arrow/compute/row/compare_internal_avx2.cc
+++ b/cpp/src/arrow/compute/row/compare_internal_avx2.cc
@@ -39,12 +39,14 @@ template <bool use_selection>
 uint32_t KeyCompare::NullUpdateColumnToRowImp_avx2(
     uint32_t id_col, uint32_t num_rows_to_compare, const uint16_t* 
sel_left_maybe_null,
     const uint32_t* left_to_right_map, LightContext* ctx, const 
KeyColumnArray& col,
-    const RowTableImpl& rows, uint8_t* match_bytevector) {
+    const RowTableImpl& rows, bool are_cols_in_encoding_order,
+    uint8_t* match_bytevector) {
   if (!rows.has_any_nulls(ctx) && !col.data(0)) {
     return num_rows_to_compare;
   }
 
-  uint32_t null_bit_id = rows.metadata().pos_after_encoding(id_col);
+  const uint32_t null_bit_id =
+      ColIdInEncodingOrder(rows, id_col, are_cols_in_encoding_order);
 
   if (!col.data(0)) {
     // Remove rows from the result for which the column value is a null
@@ -569,7 +571,7 @@ uint32_t KeyCompare::NullUpdateColumnToRow_avx2(
     bool use_selection, uint32_t id_col, uint32_t num_rows_to_compare,
     const uint16_t* sel_left_maybe_null, const uint32_t* left_to_right_map,
     LightContext* ctx, const KeyColumnArray& col, const RowTableImpl& rows,
-    uint8_t* match_bytevector) {
+    bool are_cols_in_encoding_order, uint8_t* match_bytevector) {
   int64_t num_rows_safe =
       TailSkipForSIMD::FixBitAccess(sizeof(uint32_t), col.length(), 
col.bit_offset(0));
   if (sel_left_maybe_null) {
@@ -580,13 +582,13 @@ uint32_t KeyCompare::NullUpdateColumnToRow_avx2(
   }
 
   if (use_selection) {
-    return NullUpdateColumnToRowImp_avx2<true>(id_col, num_rows_to_compare,
-                                               sel_left_maybe_null, 
left_to_right_map,
-                                               ctx, col, rows, 
match_bytevector);
+    return NullUpdateColumnToRowImp_avx2<true>(
+        id_col, num_rows_to_compare, sel_left_maybe_null, left_to_right_map, 
ctx, col,
+        rows, are_cols_in_encoding_order, match_bytevector);
   } else {
-    return NullUpdateColumnToRowImp_avx2<false>(id_col, num_rows_to_compare,
-                                                sel_left_maybe_null, 
left_to_right_map,
-                                                ctx, col, rows, 
match_bytevector);
+    return NullUpdateColumnToRowImp_avx2<false>(
+        id_col, num_rows_to_compare, sel_left_maybe_null, left_to_right_map, 
ctx, col,
+        rows, are_cols_in_encoding_order, match_bytevector);
   }
 }
 
diff --git a/cpp/src/arrow/compute/row/grouper_test.cc 
b/cpp/src/arrow/compute/row/grouper_test.cc
new file mode 100644
index 0000000000..1e853be5e4
--- /dev/null
+++ b/cpp/src/arrow/compute/row/grouper_test.cc
@@ -0,0 +1,68 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <numeric>
+
+#include "arrow/compute/exec.h"
+#include "arrow/compute/row/grouper.h"
+#include "arrow/testing/gtest_util.h"
+#include "arrow/testing/random.h"
+
+namespace arrow {
+namespace compute {
+
+// Specialized case for GH-40997
+TEST(Grouper, ResortedColumnsWithLargeNullRows) {
+  const uint64_t num_rows = 1024;
+
+  // construct random array with plenty of null values
+  const int32_t kSeed = 42;
+  const int32_t min = 0;
+  const int32_t max = 100;
+  const double null_probability = 0.3;
+  const double true_probability = 0.5;
+  auto rng = random::RandomArrayGenerator(kSeed);
+  auto b_arr = rng.Boolean(num_rows, true_probability, null_probability);
+  auto i32_arr = rng.Int32(num_rows, min, max, null_probability);
+  auto i64_arr = rng.Int64(num_rows, min, max * 10, null_probability);
+
+  // construct batches with columns which will be resorted in the grouper make
+  std::vector<ExecBatch> exec_batches = {ExecBatch({i64_arr, i32_arr, b_arr}, 
num_rows),
+                                         ExecBatch({i32_arr, i64_arr, b_arr}, 
num_rows),
+                                         ExecBatch({i64_arr, b_arr, i32_arr}, 
num_rows),
+                                         ExecBatch({i32_arr, b_arr, i64_arr}, 
num_rows),
+                                         ExecBatch({b_arr, i32_arr, i64_arr}, 
num_rows),
+                                         ExecBatch({b_arr, i64_arr, i32_arr}, 
num_rows)};
+
+  const int num_batches = static_cast<int>(exec_batches.size());
+  std::vector<uint32_t> group_num_vec;
+  group_num_vec.reserve(num_batches);
+
+  for (const auto& exec_batch : exec_batches) {
+    ExecSpan span(exec_batch);
+    ASSERT_OK_AND_ASSIGN(auto grouper, Grouper::Make(span.GetTypes()));
+    ASSERT_OK_AND_ASSIGN(Datum group_ids, grouper->Consume(span));
+    group_num_vec.emplace_back(grouper->num_groups());
+  }
+
+  for (int i = 1; i < num_batches; i++) {
+    ASSERT_EQ(group_num_vec[i - 1], group_num_vec[i]);
+  }
+}
+
+}  // namespace compute
+}  // namespace arrow
diff --git a/cpp/src/arrow/compute/row/row_internal.cc 
b/cpp/src/arrow/compute/row/row_internal.cc
index f6a62c09fc..469205e9b0 100644
--- a/cpp/src/arrow/compute/row/row_internal.cc
+++ b/cpp/src/arrow/compute/row/row_internal.cc
@@ -66,7 +66,8 @@ void RowTableMetadata::FromColumnMetadataVector(
   //
   // Columns are sorted based on the size in bytes of their fixed-length part.
   // For the varying-length column, the fixed-length part is the 32-bit field 
storing
-  // cumulative length of varying-length fields.
+  // cumulative length of varying-length fields. This is to make the memory 
access of
+  // each individual column within the encoded row alignment-friendly.
   //
   // The rules are:
   //

Reply via email to