dbms: Set: removed special case for small sets (not worth in most cases) [#METR-2944].

This commit is contained in:
Alexey Milovidov 2015-03-02 08:41:21 +03:00
parent 0c14504781
commit ab0e29f0f2
2 changed files with 33 additions and 202 deletions

View File

@ -12,7 +12,6 @@
#include <DB/Parsers/IAST.h>
#include <DB/Common/HashTable/HashSet.h>
#include <DB/Common/HashTable/SmallTable.h>
#include <DB/Interpreters/AggregationCommon.h>
#include <DB/Interpreters/Limits.h>
#include <DB/Columns/ColumnConst.h>
@ -29,13 +28,6 @@ namespace DB
* Используются в качестве параметра шаблона.
*/
template <typename Src, typename Dst>
void copyHashTable(const Src & src, Dst & dst)
{
for (const auto & x : src)
dst.insert(x);
}
/// Для случая, когда есть один числовой ключ.
template <typename FieldType, typename TData> /// UInt8/16/32/64 для любых типов соответствующей битности.
@ -46,11 +38,6 @@ struct SetMethodOneNumber
Data data;
SetMethodOneNumber() {}
template <typename Other>
SetMethodOneNumber(const Other & other) { copyHashTable(other.data, data); }
/// Для использования одного Method в разных потоках, используйте разные State.
struct State
{
@ -90,11 +77,6 @@ struct SetMethodString
Data data;
SetMethodString() {}
template <typename Other>
SetMethodString(const Other & other) { copyHashTable(other.data, data); }
struct State
{
const ColumnString::Offsets_t * offsets;
@ -136,11 +118,6 @@ struct SetMethodFixedString
Data data;
SetMethodFixedString() {}
template <typename Other>
SetMethodFixedString(const Other & other) { copyHashTable(other.data, data); }
struct State
{
size_t n;
@ -180,11 +157,6 @@ struct SetMethodKeysFixed
Data data;
SetMethodKeysFixed() {}
template <typename Other>
SetMethodKeysFixed(const Other & other) { copyHashTable(other.data, data); }
struct State
{
void init(const ConstColumnPlainPtrs & key_columns)
@ -214,11 +186,6 @@ struct SetMethodHashed
Data data;
SetMethodHashed() {}
template <typename Other>
SetMethodHashed(const Other & other) { copyHashTable(other.data, data); }
struct State
{
void init(const ConstColumnPlainPtrs & key_columns)
@ -248,6 +215,10 @@ struct SetVariants
std::unique_ptr<SetMethodOneNumber<UInt8, HashSet<UInt8, TrivialHash, HashTableFixedGrower<8>>>> key8;
std::unique_ptr<SetMethodOneNumber<UInt16, HashSet<UInt16, TrivialHash, HashTableFixedGrower<16>>>> key16;
/** Также для эксперимента проверялась возможность использовать SmallSet,
* пока количество элементов в множестве небольшое (и, при необходимости, конвертировать в полноценный HashSet).
* Но этот эксперимент показал, что преимущество есть только в редких случаях.
*/
std::unique_ptr<SetMethodOneNumber<UInt32, HashSet<UInt32, HashCRC32<UInt32>>>> key32;
std::unique_ptr<SetMethodOneNumber<UInt64, HashSet<UInt64, HashCRC32<UInt64>>>> key64;
std::unique_ptr<SetMethodString<HashSetWithSavedHash<StringRef>>> key_string;
@ -256,35 +227,16 @@ struct SetVariants
std::unique_ptr<SetMethodKeysFixed<HashSet<UInt256, UInt256HashCRC32>>> keys256;
std::unique_ptr<SetMethodHashed<HashSet<UInt128, UInt128TrivialHash>>> hashed;
std::unique_ptr<SetMethodOneNumber<UInt32, SmallSet<UInt32, 16>>> key32_small;
std::unique_ptr<SetMethodOneNumber<UInt64, SmallSet<UInt64, 16>>> key64_small;
std::unique_ptr<SetMethodString<SmallSet<StringRef, 16>>> key_string_small;
std::unique_ptr<SetMethodFixedString<SmallSet<StringRef, 16>>> key_fixed_string_small;
std::unique_ptr<SetMethodKeysFixed<SmallSet<UInt128, 16>>> keys128_small;
std::unique_ptr<SetMethodKeysFixed<SmallSet<UInt256, 16>>> keys256_small;
std::unique_ptr<SetMethodHashed<SmallSet<UInt128, 16>>> hashed_small;
/** В отличие от Aggregator, здесь не используется метод concat.
* Это сделано потому что метод hashed, хоть и медленнее, но в данном случае, использует меньше оперативки.
* так как при его использовании, сами значения ключей не сохраняются.
*/
Arena string_pool;
#define APPLY_FOR_SET_VARIANTS(M) \
M(key8, false) \
M(key16, false) \
M(key32, false) \
M(key64, false) \
M(key_string, false) \
M(key_fixed_string, false) \
M(keys128, false) \
M(keys256, false) \
M(hashed, false) \
M(key32_small, true) \
M(key64_small, true) \
M(key_string_small, true) \
M(key_fixed_string_small, true) \
M(keys128_small, true) \
M(keys256_small, true) \
M(hashed_small, true)
#define APPLY_FOR_SET_VARIANTS_BIG(M) \
M(key8) \
M(key16) \
M(key32) \
M(key64) \
M(key_string) \
@ -297,7 +249,7 @@ struct SetVariants
{
EMPTY,
#define M(NAME, IS_SMALL) NAME,
#define M(NAME) NAME,
APPLY_FOR_SET_VARIANTS(M)
#undef M
};
@ -313,9 +265,6 @@ struct SetVariants
size_t getTotalRowCount() const;
/// Считает размер в байтах буфера Set и размер string_pool'а
size_t getTotalByteCount() const;
bool isSmall() const;
void convertToBig();
};
@ -414,19 +363,9 @@ private:
template <typename Method>
size_t insertFromBlockImplBig(
void insertFromBlockImpl(
Method & method,
const ConstColumnPlainPtrs & key_columns,
size_t start,
size_t rows,
StringRefs & keys,
SetVariants & variants);
template <typename Method>
size_t insertFromBlockImplSmall(
Method & method,
const ConstColumnPlainPtrs & key_columns,
size_t start,
size_t rows,
StringRefs & keys,
SetVariants & variants);

View File

@ -29,7 +29,7 @@ void SetVariants::init(Type type_)
{
case Type::EMPTY: break;
#define M(NAME, IS_SMALL) \
#define M(NAME) \
case Type::NAME: NAME.reset(new decltype(NAME)::element_type); break;
APPLY_FOR_SET_VARIANTS(M)
#undef M
@ -46,7 +46,7 @@ size_t SetVariants::getTotalRowCount() const
{
case Type::EMPTY: return 0;
#define M(NAME, IS_SMALL) \
#define M(NAME) \
case Type::NAME: return NAME->data.size();
APPLY_FOR_SET_VARIANTS(M)
#undef M
@ -63,7 +63,7 @@ size_t SetVariants::getTotalByteCount() const
{
case Type::EMPTY: return 0;
#define M(NAME, IS_SMALL) \
#define M(NAME) \
case Type::NAME: return NAME->data.getBufferSizeInBytes();
APPLY_FOR_SET_VARIANTS(M)
#undef M
@ -74,42 +74,6 @@ size_t SetVariants::getTotalByteCount() const
}
bool SetVariants::isSmall() const
{
switch (type)
{
case Type::EMPTY: return false;
#define M(NAME, IS_SMALL) \
case Type::NAME: return IS_SMALL;
APPLY_FOR_SET_VARIANTS(M)
#undef M
default:
throw Exception("Unknown Set variant.", ErrorCodes::UNKNOWN_AGGREGATED_DATA_VARIANT);
}
}
void SetVariants::convertToBig()
{
switch (type)
{
#define M(NAME) \
case Type::NAME ## _small: \
NAME.reset(new decltype(NAME)::element_type(*NAME ## _small)); \
NAME ## _small.reset(); \
type = Type::NAME; \
break;
APPLY_FOR_SET_VARIANTS_BIG(M)
#undef M
default:
throw Exception("Unknown Set variant.", ErrorCodes::UNKNOWN_AGGREGATED_DATA_VARIANT);
}
}
bool Set::checkSetSizeLimits() const
{
if (max_rows && data.getTotalRowCount() > max_rows)
@ -122,9 +86,6 @@ bool Set::checkSetSizeLimits() const
SetVariants::Type SetVariants::chooseMethod(const ConstColumnPlainPtrs & key_columns, Sizes & key_sizes)
{
/** Возвращает small-методы, так как обработка начинается с них.
* Затем, в процессе работы, данные могут быть переконвертированы в полноценную (не small) структуру, если их становится много.
*/
size_t keys_size = key_columns.size();
bool all_fixed = true;
@ -150,35 +111,34 @@ SetVariants::Type SetVariants::chooseMethod(const ConstColumnPlainPtrs & key_col
if (size_of_field == 2)
return SetVariants::Type::key16;
if (size_of_field == 4)
return SetVariants::Type::key32_small;
return SetVariants::Type::key32;
if (size_of_field == 8)
return SetVariants::Type::key64_small;
return SetVariants::Type::key64;
throw Exception("Logical error: numeric column has sizeOfField not in 1, 2, 4, 8.", ErrorCodes::LOGICAL_ERROR);
}
/// Если ключи помещаются в N бит, будем использовать хэш-таблицу по упакованным в N-бит ключам
if (all_fixed && keys_bytes <= 16)
return SetVariants::Type::keys128_small;
return SetVariants::Type::keys128;
if (all_fixed && keys_bytes <= 32)
return SetVariants::Type::keys256_small;
return SetVariants::Type::keys256;
/// Если есть один строковый ключ, то используем хэш-таблицу с ним
if (keys_size == 1 && (typeid_cast<const ColumnString *>(key_columns[0]) || typeid_cast<const ColumnConstString *>(key_columns[0])))
return SetVariants::Type::key_string_small;
return SetVariants::Type::key_string;
if (keys_size == 1 && typeid_cast<const ColumnFixedString *>(key_columns[0]))
return SetVariants::Type::key_fixed_string_small;
return SetVariants::Type::key_fixed_string;
/// Иначе будем агрегировать по конкатенации ключей.
return SetVariants::Type::hashed_small;
return SetVariants::Type::hashed;
}
template <typename Method>
size_t NO_INLINE Set::insertFromBlockImplSmall(
void NO_INLINE Set::insertFromBlockImpl(
Method & method,
const ConstColumnPlainPtrs & key_columns,
size_t start,
size_t rows,
StringRefs & keys,
SetVariants & variants)
@ -188,38 +148,7 @@ size_t NO_INLINE Set::insertFromBlockImplSmall(
size_t keys_size = key_columns.size();
/// Для всех строчек
for (size_t i = start; i < rows; ++i)
{
/// Строим ключ
typename Method::Key key = state.getKey(key_columns, keys_size, i, key_sizes, keys);
typename Method::Data::iterator it = method.data.find(key);
bool inserted;
if (!method.data.tryEmplace(key, it, inserted))
return i;
if (inserted)
method.onNewKey(*it, keys_size, i, keys, variants.string_pool);
}
return rows;
}
template <typename Method>
size_t NO_INLINE Set::insertFromBlockImplBig(
Method & method,
const ConstColumnPlainPtrs & key_columns,
size_t start,
size_t rows,
StringRefs & keys,
SetVariants & variants)
{
typename Method::State state;
state.init(key_columns);
size_t keys_size = key_columns.size();
/// Для всех строчек
for (size_t i = start; i < rows; ++i)
for (size_t i = 0; i < rows; ++i)
{
/// Строим ключ
typename Method::Key key = state.getKey(key_columns, keys_size, i, key_sizes, keys);
@ -231,8 +160,6 @@ size_t NO_INLINE Set::insertFromBlockImplBig(
if (inserted)
method.onNewKey(*it, keys_size, i, keys, variants.string_pool);
}
return rows;
}
@ -258,54 +185,19 @@ bool Set::insertFromBlock(const Block & block, bool create_ordered_set)
data.init(data.chooseMethod(key_columns, key_sizes));
StringRefs keys;
size_t start = 0;
if (false) {}
else if (data.type == SetVariants::Type::key8)
start = insertFromBlockImplBig(*data.key8, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key16)
start = insertFromBlockImplBig(*data.key16, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key32_small)
start = insertFromBlockImplSmall(*data.key32_small, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key64_small)
start = insertFromBlockImplSmall(*data.key64_small, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key_string_small)
start = insertFromBlockImplSmall(*data.key_string_small, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key_fixed_string_small)
start = insertFromBlockImplSmall(*data.key_fixed_string_small, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::keys128_small)
start = insertFromBlockImplSmall(*data.keys128_small, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::keys256_small)
start = insertFromBlockImplSmall(*data.keys256_small, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::hashed_small)
start = insertFromBlockImplSmall(*data.hashed_small, key_columns, start, rows, keys, data);
/// Нужно ли сконвертировать из small в полноценный вариант.
if (data.isSmall() && start != rows)
data.convertToBig();
if (false) {}
else if (data.type == SetVariants::Type::key32)
start = insertFromBlockImplBig(*data.key32, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key64)
start = insertFromBlockImplBig(*data.key64, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key_string)
start = insertFromBlockImplBig(*data.key_string, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::key_fixed_string)
start = insertFromBlockImplBig(*data.key_fixed_string, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::keys128)
start = insertFromBlockImplBig(*data.keys128, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::keys256)
start = insertFromBlockImplBig(*data.keys256, key_columns, start, rows, keys, data);
else if (data.type == SetVariants::Type::hashed)
start = insertFromBlockImplBig(*data.hashed, key_columns, start, rows, keys, data);
if (start == 0)
#define M(NAME) \
else if (data.type == SetVariants::Type::NAME) \
insertFromBlockImpl(*data.NAME, key_columns, rows, keys, data);
APPLY_FOR_SET_VARIANTS(M)
#undef M
else
throw Exception("Unknown set variant.", ErrorCodes::UNKNOWN_SET_DATA_VARIANT);
if (create_ordered_set)
for (size_t i = 0; i < rows; ++i)
ordered_set_elements->push_back((*key_columns[0])[i]);
ordered_set_elements->push_back((*key_columns[0])[i]); /// ordered_set для индекса работает только если IN одному ключу.
if (!checkSetSizeLimits())
{
@ -637,7 +529,7 @@ void Set::executeOrdinary(const ConstColumnPlainPtrs & key_columns, ColumnUInt8:
StringRefs keys;
if (false) {}
#define M(NAME, IS_SMALL) \
#define M(NAME) \
else if (data.type == SetVariants::Type::NAME) \
executeImpl(*data.NAME, key_columns, vec_res, negative, rows, keys);
APPLY_FOR_SET_VARIANTS(M)
@ -654,7 +546,7 @@ void Set::executeArray(const ColumnArray * key_column, ColumnUInt8::Container_t
StringRefs keys;
if (false) {}
#define M(NAME, IS_SMALL) \
#define M(NAME) \
else if (data.type == SetVariants::Type::NAME) \
executeArrayImpl(*data.NAME, ConstColumnPlainPtrs{key_column}, offsets, vec_res, negative, rows, keys);
APPLY_FOR_SET_VARIANTS(M)