From 58bfe0dbdea5ffb88e8a88b5396fc798bbbfc1e2 Mon Sep 17 00:00:00 2001 From: Mryange Date: Tue, 28 Jul 2026 10:39:16 +0800 Subject: [PATCH] [opt](exec) optimize fixed key packing (#65572) ### What problem does this PR solve? Fixed-key packing could lose `restrict` alias information when the source data pointer was captured by reference in an outlined lambda, producing a less efficient copy loop. This change obtains the restricted pointer inside the lambda after nullable key replacement, preserving alias information without changing the key encoding or query behavior. (cherry picked from commit 0127314e7a50fd43fa9a8b959bb73ba600fb304b) --- be/src/exec/common/hash_table/hash_map_context.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/be/src/exec/common/hash_table/hash_map_context.h b/be/src/exec/common/hash_table/hash_map_context.h index 121008cd8301e2..49f3050dc9b700 100644 --- a/be/src/exec/common/hash_table/hash_map_context.h +++ b/be/src/exec/common/hash_table/hash_map_context.h @@ -943,17 +943,21 @@ struct MethodKeysFixed : public MethodBase { } for (size_t j = 0; j < key_columns.size(); ++j) { - const char* __restrict data = key_columns[j]->get_raw_data().data; - auto goo = [&](Fixed zero) { CHECK_EQ(sizeof(Fixed), key_sizes[j]); if (has_null_column.size() && has_null_column[j]) { const auto* nullmap = assert_cast(*nullmap_columns[j]).get_data().data(); // make sure null cell is filled by 0x0 + // This mutates the same underlying buffer returned by get_raw_data(), so get + // the restricted data pointer only after the replacement finishes. const_cast(key_columns[j])->replace_column_null_data(nullmap); } auto* __restrict current = result_data + offset; + // Do not hoist data out of goo. A reference capture is lowered to an ordinary + // closure field, so restrict/noalias information is lost when goo is not inlined, + // which prevents the copy loop from being optimized. + const char* __restrict data = key_columns[j]->get_raw_data().data; for (size_t i = 0; i < row_numbers; ++i) { memcpy_fixed(current, data); current += sizeof(T); @@ -965,6 +969,7 @@ struct MethodKeysFixed : public MethodBase { // Also verify that the stride sizeof(T) is a multiple of alignof(Fixed), // otherwise alignment will be lost on subsequent loop iterations // (e.g. UInt96 has sizeof=12, stride 12 is not a multiple of alignof(uint64_t)=8). + const char* data = key_columns[j]->get_raw_data().data; if (sizeof(T) % alignof(Fixed) == 0 && reinterpret_cast(result_data + offset) % alignof(Fixed) == 0 && reinterpret_cast(data) % alignof(Fixed) == 0) {