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
3940namespace 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.
4347template <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
126199protected:
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 ());
0 commit comments