github-actions[bot] commented on code in PR #65635:
URL: https://github.com/apache/doris/pull/65635#discussion_r3586019151


##########
be/src/exec/common/hash_table/hash_table_set_build.h:
##########
@@ -41,11 +42,15 @@ struct HashTableBuild {
         };
         auto creator_for_null_key = [&](auto& mapped) { mapped = {k}; };
 
-        for (; k < _rows; ++k) {
-            if (k % CHECK_FRECUENCY == 0) {
-                RETURN_IF_CANCELLED(_state);
+        RETURN_IF_CANCELLED(_state);
+        // The batch path regresses StringRef keys, including serialized and 
nullable strings.
+        if constexpr (std::is_same_v<typename HashTableContext::Key, 
StringRef>) {

Review Comment:
   The existing cancellation thread covers the new non-string 
`lazy_emplace_batch_void` branch, but the `StringRef` fallback here has the 
same loss of responsiveness. Before this PR the build loop checked 
`RETURN_IF_CANCELLED` every `CHECK_FRECUENCY` rows for all key types; now 
string/serialized/nullable-string keys check only once at line 45 and then run 
this whole loop. Since the set sink has already merged the full build side 
before `_process_build_block`, a cancelled query with a large string build 
input can still spend a long time inserting rows. Please restore the periodic 
cancellation check in this fallback loop as well, not only in the batch helper.



##########
be/benchmark/benchmark_set_hash_table_inline.hpp:
##########
@@ -0,0 +1,421 @@
+// 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.
+
+#pragma once
+
+#include <benchmark/benchmark.h>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <optional>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "common/compiler_util.h"
+#include "core/column/column_nullable.h"
+#include "core/column/column_vector.h"
+#include "exec/common/set_utils.h"
+
+namespace doris {
+
+namespace {
+
+enum class SetHashTableInlineOperation {
+    BUILD_COLD,
+    BUILD_ALL_HIT,
+    PROBE_ALL_HIT,
+    PROBE_MISS_KEYS,
+    PROBE_MIXED_KEYS_50_PERCENT,
+};
+
+enum class SetHashTableExecutionMode {
+    ROW,
+    // String sub-table groups are prepared outside the timed region.
+    BATCH_CORE,
+    // String sub-table grouping and hash-table operations are both timed.
+    BATCH_WITH_GROUPING,
+};
+
+template <typename Method>
+class SetHashTableMethodAdapterBase : public Method {
+public:
+    using Method::Method;
+
+protected:
+    template <typename State>
+    auto ALWAYS_INLINE find_impl(State& state, size_t i) {
+        this->template prefetch<true>(i);
+        return state.find_key_with_hash(*this->hash_table, i, this->keys[i], 
this->hash_values[i]);
+    }
+
+    template <typename State, typename F, typename FF>
+    auto ALWAYS_INLINE lazy_emplace_impl(State& state, size_t i, F&& creator,
+                                         FF&& creator_for_null_key) {
+        this->template prefetch<false>(i);
+        return state.lazy_emplace_key(*this->hash_table, i, this->keys[i], 
this->hash_values[i],
+                                      creator, creator_for_null_key);
+    }
+};
+
+template <typename Method>
+class SetHashTableNoInlineMethod : public 
SetHashTableMethodAdapterBase<Method> {
+public:
+    using SetHashTableMethodAdapterBase<Method>::SetHashTableMethodAdapterBase;
+
+    template <typename State>
+    auto NO_INLINE find(State& state, size_t i) {
+        return this->find_impl(state, i);
+    }
+
+    template <typename State, typename F, typename FF>
+    auto NO_INLINE lazy_emplace(State& state, size_t i, F&& creator, FF&& 
creator_for_null_key) {
+        return this->lazy_emplace_impl(state, i, std::forward<F>(creator),
+                                       std::forward<FF>(creator_for_null_key));
+    }
+};
+
+template <typename Method>
+class SetHashTableAlwaysInlineMethod : public 
SetHashTableMethodAdapterBase<Method> {
+public:
+    using SetHashTableMethodAdapterBase<Method>::SetHashTableMethodAdapterBase;
+
+    template <typename State>
+    auto ALWAYS_INLINE find(State& state, size_t i) {
+        return this->find_impl(state, i);
+    }
+
+    template <typename State, typename F, typename FF>
+    auto ALWAYS_INLINE lazy_emplace(State& state, size_t i, F&& creator,
+                                    FF&& creator_for_null_key) {
+        return this->lazy_emplace_impl(state, i, std::forward<F>(creator),
+                                       std::forward<FF>(creator_for_null_key));
+    }
+};
+
+template <typename Key>
+struct SetHashTableInlineKeyStorage {
+    std::vector<Key> keys;
+
+    SetHashTableInlineKeyStorage(size_t rows, uint64_t seed) {
+        keys.reserve(rows);
+        for (size_t i = 0; i < rows; ++i) {
+            const uint64_t value = seed + i;
+            if constexpr (std::is_same_v<Key, UInt64>) {
+                keys.emplace_back(value);
+            } else if constexpr (std::is_same_v<Key, UInt104>) {
+                keys.emplace_back(UInt104 {.a = static_cast<UInt8>(value),
+                                           .b = static_cast<UInt32>(value >> 
8),
+                                           .c = value * 
0x9e3779b97f4a7c15ULL});
+            } else {
+                static_assert(std::is_same_v<Key, UInt64> || 
std::is_same_v<Key, UInt104>);
+            }
+        }
+    }
+};
+
+template <>
+struct SetHashTableInlineKeyStorage<StringRef> {
+    static constexpr size_t STRING_SIZE = 16;
+
+    std::vector<std::array<char, STRING_SIZE>> bytes;
+    std::vector<StringRef> keys;
+
+    SetHashTableInlineKeyStorage(size_t rows, uint64_t seed) : bytes(rows) {
+        keys.reserve(rows);
+        for (size_t i = 0; i < rows; ++i) {
+            const uint64_t value = seed + i;
+            const uint64_t mixed = value * 0x9e3779b97f4a7c15ULL;
+            std::memcpy(bytes[i].data(), &value, sizeof(value));
+            std::memcpy(bytes[i].data() + sizeof(value), &mixed, 
sizeof(mixed));
+            keys.emplace_back(bytes[i].data(), STRING_SIZE);
+        }
+    }
+};
+
+template <typename Method, bool nullable>
+class SetHashTableInlineFixture {
+public:
+    using Key = typename Method::Key;
+    using State = typename Method::State;
+
+    explicit SetHashTableInlineFixture(size_t rows)
+            : _rows(rows),
+              _hit_storage(rows, 1),
+              _miss_storage(rows, rows * 4 + 1),
+              _method(create_method()) {
+        _mixed_keys.reserve(rows);
+        for (size_t i = 0; i < rows; ++i) {
+            _mixed_keys.emplace_back(i % 2 == 0 ? _hit_storage.keys[i] : 
_miss_storage.keys[i]);
+        }
+
+        if constexpr (nullable) {
+            auto nested_column = ColumnInt64::create();
+            auto null_map_column = ColumnUInt8::create();
+            auto& nested_data = nested_column->get_data();
+            auto& null_map = null_map_column->get_data();
+            nested_data.reserve(rows);
+            null_map.reserve(rows);
+            for (size_t i = 0; i < rows; ++i) {
+                nested_data.push_back(static_cast<UInt64>(i + 1));
+                null_map.push_back(i % 100 == 0);
+            }
+            _nullable_column =
+                    ColumnNullable::create(std::move(nested_column), 
std::move(null_map_column));
+            _state_columns.push_back(_nullable_column.get());
+        }
+        _state.emplace(_state_columns);
+        select_keys(_hit_storage.keys);
+    }
+
+    void prepare_empty_table() {
+        _method->hash_table->clear_and_shrink();
+        _arena.clear();
+        _method->hash_table->reserve(_rows);
+        select_keys(_hit_storage.keys);
+    }
+
+    void prepare_filled_table(const std::vector<Key>& probe_keys) {
+        prepare_empty_table();
+        fill_table();
+        select_keys(probe_keys);
+    }
+
+    const std::vector<Key>& hit_keys() const { return _hit_storage.keys; }
+    const std::vector<Key>& miss_keys() const { return _miss_storage.keys; }
+    const std::vector<Key>& mixed_keys() const { return _mixed_keys; }
+
+    void prepare_batch_groups() {

Review Comment:
   This benchmark does not actually exercise the string sub-table grouping path 
it labels. `SetHashTableInlineString` is `SetMethodOneString`, which is 
PHHashMap-backed, while `Method::is_string_hash_map()` only returns true for 
the `StringHashMap` variants. As a result `prepare_batch_groups()` is a no-op 
here, and the StringRef `BatchCore` / `BatchWithGrouping` registrations fall 
through to the generic batch loop rather than timing prebuilt vs in-timed 
string grouping. That makes the new benchmark results misleading for the string 
batching regression this file is trying to measure; please either register a 
real `StringHashMap` method for these modes or rename/remove the grouping 
modes/comments for the set string context.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to