Skip to content

Commit 7340ae9

Browse files
executor: Improve HashAgg string collation key caching (#10978)
close #10979
1 parent ebb6c8c commit 7340ae9

6 files changed

Lines changed: 584 additions & 10 deletions

File tree

‎dbms/src/Common/ColumnsHashing.h‎

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <Columns/ColumnString.h>
2020
#include <Common/Arena.h>
2121
#include <Common/ColumnsHashingImpl.h>
22+
#include <Common/HashTable/HashMap.h>
2223
#include <Common/HashTable/HashTable.h>
2324
#include <Common/HashTable/HashTableKeyHolder.h>
2425
#include <Common/assert_cast.h>
@@ -38,6 +39,9 @@ extern const int LOGICAL_ERROR;
3839

3940
namespace ColumnsHashing
4041
{
42+
static constexpr size_t min_rows_to_use_collation_sort_key_cache = 1024;
43+
static constexpr size_t max_collation_sort_key_cache_distinct_ratio = 32;
44+
4145
/// For the case when there is one numeric key.
4246
/// UInt8/16/32/64 for any type with corresponding bit width.
4347
template <typename Value, typename Mapped, typename FieldType, bool use_cache = true>
@@ -95,6 +99,69 @@ class KeyStringBatchHandlerBase
9599
size_t processed_row_idx = 0;
96100
std::vector<String> sort_key_containers{};
97101
std::vector<StringRef> batch_rows{};
102+
bool collation_sort_key_cache_active = false;
103+
bool collation_sort_key_cache_fallback = false;
104+
size_t collation_sort_key_cache_distinct_limit = 0;
105+
HashMap<StringRef, size_t, StringRefHash> raw_to_sort_key_index;
106+
std::vector<String> cached_sort_keys{};
107+
108+
template <typename DerivedCollator>
109+
StringRef getSortKeyWithCache(
110+
const DerivedCollator & collator,
111+
const StringRef & raw_key,
112+
std::string & sort_key_container)
113+
{
114+
if unlikely (!collation_sort_key_cache_active)
115+
return collator.sortKey(raw_key.data, raw_key.size, sort_key_container);
116+
117+
if (auto it = raw_to_sort_key_index.find(raw_key); it != nullptr)
118+
{
119+
const auto & sort_key = cached_sort_keys[it->getMapped()];
120+
return {sort_key.data(), sort_key.size()};
121+
}
122+
123+
if (raw_to_sort_key_index.size() >= collation_sort_key_cache_distinct_limit)
124+
{
125+
collation_sort_key_cache_active = false;
126+
collation_sort_key_cache_fallback = true;
127+
return collator.sortKey(raw_key.data, raw_key.size, sort_key_container);
128+
}
129+
130+
const auto sort_key_index = cached_sort_keys.size();
131+
auto & cached_sort_key = cached_sort_keys.emplace_back();
132+
const auto sort_key_size = collator.sortKey(raw_key.data, raw_key.size, cached_sort_key).size;
133+
// Collators may resize the container to their worst-case output size.
134+
cached_sort_key.resize(sort_key_size);
135+
136+
typename decltype(raw_to_sort_key_index)::LookupResult it;
137+
bool inserted = false;
138+
raw_to_sort_key_index.emplace(raw_key, it, inserted);
139+
RUNTIME_CHECK(inserted);
140+
it->getMapped() = sort_key_index;
141+
142+
return {cached_sort_key.data(), cached_sort_key.size()};
143+
}
144+
145+
template <typename DerivedCollator, bool use_cache>
146+
void prepareNextBatchWithCollator(
147+
const UInt8 * chars,
148+
const IColumn::Offsets & offsets,
149+
size_t cur_batch_size,
150+
const DerivedCollator & collator)
151+
{
152+
for (size_t i = 0; i < cur_batch_size; ++i)
153+
{
154+
const auto row = processed_row_idx + i;
155+
const auto last_offset = offsets[row - 1];
156+
// Remove last zero byte.
157+
StringRef key(chars + last_offset, offsets[row] - last_offset - 1);
158+
if constexpr (use_cache)
159+
key = getSortKeyWithCache(collator, key, sort_key_containers[i]);
160+
else
161+
key = collator.sortKey(key.data, key.size, sort_key_containers[i]);
162+
batch_rows[i] = key;
163+
}
164+
}
98165

99166
template <typename DerivedCollator, bool has_collator>
100167
void prepareNextBatchType(
@@ -108,23 +175,41 @@ class KeyStringBatchHandlerBase
108175

109176
batch_rows.resize(cur_batch_size);
110177

111-
const auto * derived_collator = static_cast<const DerivedCollator *>(collator);
112-
for (size_t i = 0; i < cur_batch_size; ++i)
178+
if constexpr (has_collator)
113179
{
114-
const auto row = processed_row_idx + i;
115-
const auto last_offset = offsets[row - 1];
116-
// Remove last zero byte.
117-
StringRef key(chars + last_offset, offsets[row] - last_offset - 1);
118-
if constexpr (has_collator)
119-
key = derived_collator->sortKey(key.data, key.size, sort_key_containers[i]);
120-
121-
batch_rows[i] = key;
180+
const auto & derived_collator = *static_cast<const DerivedCollator *>(collator);
181+
if (collation_sort_key_cache_active)
182+
prepareNextBatchWithCollator<DerivedCollator, true>(chars, offsets, cur_batch_size, derived_collator);
183+
else
184+
prepareNextBatchWithCollator<DerivedCollator, false>(chars, offsets, cur_batch_size, derived_collator);
185+
}
186+
else
187+
{
188+
for (size_t i = 0; i < cur_batch_size; ++i)
189+
{
190+
const auto row = processed_row_idx + i;
191+
const auto last_offset = offsets[row - 1];
192+
// Remove last zero byte.
193+
batch_rows[i] = StringRef(chars + last_offset, offsets[row] - last_offset - 1);
194+
}
122195
}
123196
processed_row_idx += cur_batch_size;
124197
}
125198

126199
protected:
127200
bool inited() const { return !sort_key_containers.empty(); }
201+
bool hasCollationSortKeyCacheFallback() const { return collation_sort_key_cache_fallback; }
202+
203+
void initCollationSortKeyCache(size_t rows)
204+
{
205+
if (rows < min_rows_to_use_collation_sort_key_cache)
206+
return;
207+
208+
collation_sort_key_cache_active = true;
209+
collation_sort_key_cache_distinct_limit = rows / max_collation_sort_key_cache_distinct_ratio;
210+
raw_to_sort_key_index.reserve(collation_sort_key_cache_distinct_limit + 1);
211+
cached_sort_keys.reserve(collation_sort_key_cache_distinct_limit + 1);
212+
}
128213

129214
void init(size_t start_row, size_t max_batch_size)
130215
{
@@ -207,6 +292,14 @@ struct HashMethodString
207292
collator = collators[0];
208293
}
209294

295+
void initCollationSortKeyCache(size_t rows)
296+
{
297+
if (collator != nullptr && collator->isCI())
298+
BatchHandlerBase::initCollationSortKeyCache(rows);
299+
}
300+
301+
bool hasCollationSortKeyCacheFallback() const { return BatchHandlerBase::hasCollationSortKeyCacheFallback(); }
302+
210303
void initBatchHandler(size_t start_row, size_t max_batch_size)
211304
{
212305
assert(!BatchHandlerBase::inited());

‎dbms/src/Common/ColumnsHashingImpl.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ class HashMethodBase
129129
using Cache = LastElementCache<Value, consecutive_keys_optimization>;
130130
using Derived = TDerived;
131131

132+
void initCollationSortKeyCache(size_t) {}
133+
bool hasCollationSortKeyCacheFallback() const { return false; }
134+
132135
template <typename Data>
133136
ALWAYS_INLINE inline EmplaceResult emplaceKey(
134137
Data & data,

0 commit comments

Comments
 (0)