2015-01-08 21:41:35 +00:00
|
|
|
|
#include <DB/Interpreters/Aggregator.h>
|
|
|
|
|
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionCount.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionSum.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionAvg.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionsMinMaxAny.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionsArgMinMax.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionUniq.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionUniqUpTo.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionGroupArray.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionGroupUniqArray.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionQuantile.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionQuantileTiming.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionIf.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionArray.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionState.h>
|
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionMerge.h>
|
2016-10-21 14:57:43 +00:00
|
|
|
|
#include <DB/AggregateFunctions/AggregateFunctionNull.h>
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Шаблон цикла агрегации, позволяющий сгенерировать специализированный вариант для конкретной комбинации агрегатных функций.
|
|
|
|
|
* Отличается от обычного тем, что вызовы агрегатных функций должны инлайниться, а цикл обновления агрегатных функций должен развернуться.
|
|
|
|
|
*
|
|
|
|
|
* Так как возможных комбинаций слишком много, то не представляется возможным сгенерировать их все заранее.
|
|
|
|
|
* Этот шаблон предназначен для того, чтобы инстанцировать его в рантайме,
|
|
|
|
|
* путём запуска компилятора, компиляции shared library и использования её с помощью dlopen.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Список типов - для удобного перечисления агрегатных функций.
|
|
|
|
|
*/
|
2015-01-18 01:11:32 +00:00
|
|
|
|
template <typename... TTail>
|
2015-01-08 21:41:35 +00:00
|
|
|
|
struct TypeList
|
|
|
|
|
{
|
2015-01-18 01:11:32 +00:00
|
|
|
|
static constexpr size_t size = 0;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
template <size_t I>
|
2015-01-18 01:11:32 +00:00
|
|
|
|
using At = std::nullptr_t;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
template <typename Func, size_t index = 0>
|
2015-01-18 01:11:32 +00:00
|
|
|
|
static void forEach(Func && func)
|
2015-01-08 21:41:35 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2015-01-18 01:11:32 +00:00
|
|
|
|
template <typename THead, typename... TTail>
|
|
|
|
|
struct TypeList<THead, TTail...>
|
2015-01-08 21:41:35 +00:00
|
|
|
|
{
|
|
|
|
|
using Head = THead;
|
2015-01-18 01:11:32 +00:00
|
|
|
|
using Tail = TypeList<TTail...>;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
2015-01-18 01:11:32 +00:00
|
|
|
|
static constexpr size_t size = 1 + sizeof...(TTail);
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
template <size_t I>
|
2015-01-18 01:11:32 +00:00
|
|
|
|
using At = typename std::template conditional<I == 0, Head, typename Tail::template At<I - 1>>::type;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
template <typename Func, size_t index = 0>
|
2015-01-11 02:00:26 +00:00
|
|
|
|
static void ALWAYS_INLINE forEach(Func && func)
|
2015-01-08 21:41:35 +00:00
|
|
|
|
{
|
|
|
|
|
func.template operator()<Head, index>();
|
2015-01-18 01:11:32 +00:00
|
|
|
|
Tail::template forEach<Func, index + 1>(std::forward<Func>(func));
|
2015-01-08 21:41:35 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct AggregateFunctionsUpdater
|
|
|
|
|
{
|
|
|
|
|
AggregateFunctionsUpdater(
|
|
|
|
|
const Aggregator::AggregateFunctionsPlainPtrs & aggregate_functions_,
|
|
|
|
|
const Sizes & offsets_of_aggregate_states_,
|
|
|
|
|
Aggregator::AggregateColumns & aggregate_columns_,
|
|
|
|
|
AggregateDataPtr & value_,
|
2016-09-21 16:39:44 +00:00
|
|
|
|
size_t row_num_,
|
|
|
|
|
Arena * arena_)
|
2015-01-08 21:41:35 +00:00
|
|
|
|
: aggregate_functions(aggregate_functions_),
|
|
|
|
|
offsets_of_aggregate_states(offsets_of_aggregate_states_),
|
|
|
|
|
aggregate_columns(aggregate_columns_),
|
2016-09-19 22:30:40 +00:00
|
|
|
|
value(value_), row_num(row_num_), arena(arena_)
|
2015-01-08 21:41:35 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename AggregateFunction, size_t column_num>
|
|
|
|
|
void operator()() ALWAYS_INLINE;
|
|
|
|
|
|
|
|
|
|
const Aggregator::AggregateFunctionsPlainPtrs & aggregate_functions;
|
|
|
|
|
const Sizes & offsets_of_aggregate_states;
|
|
|
|
|
Aggregator::AggregateColumns & aggregate_columns;
|
|
|
|
|
AggregateDataPtr & value;
|
|
|
|
|
size_t row_num;
|
2016-09-19 22:30:40 +00:00
|
|
|
|
Arena * arena;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename AggregateFunction, size_t column_num>
|
|
|
|
|
void AggregateFunctionsUpdater::operator()()
|
|
|
|
|
{
|
2015-01-13 03:03:45 +00:00
|
|
|
|
static_cast<AggregateFunction *>(aggregate_functions[column_num])->add(
|
2015-01-08 21:41:35 +00:00
|
|
|
|
value + offsets_of_aggregate_states[column_num],
|
|
|
|
|
&aggregate_columns[column_num][0],
|
2016-09-19 22:30:40 +00:00
|
|
|
|
row_num, arena);
|
2015-01-08 21:41:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct AggregateFunctionsCreator
|
|
|
|
|
{
|
|
|
|
|
AggregateFunctionsCreator(
|
|
|
|
|
const Aggregator::AggregateFunctionsPlainPtrs & aggregate_functions_,
|
|
|
|
|
const Sizes & offsets_of_aggregate_states_,
|
|
|
|
|
Aggregator::AggregateColumns & aggregate_columns_,
|
|
|
|
|
AggregateDataPtr & aggregate_data_)
|
|
|
|
|
: aggregate_functions(aggregate_functions_),
|
|
|
|
|
offsets_of_aggregate_states(offsets_of_aggregate_states_),
|
|
|
|
|
aggregate_data(aggregate_data_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename AggregateFunction, size_t column_num>
|
2015-01-11 02:00:26 +00:00
|
|
|
|
void operator()() ALWAYS_INLINE;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
const Aggregator::AggregateFunctionsPlainPtrs & aggregate_functions;
|
|
|
|
|
const Sizes & offsets_of_aggregate_states;
|
|
|
|
|
AggregateDataPtr & aggregate_data;
|
|
|
|
|
};
|
|
|
|
|
|
2015-01-11 02:00:26 +00:00
|
|
|
|
template <typename AggregateFunction, size_t column_num>
|
|
|
|
|
void AggregateFunctionsCreator::operator()()
|
|
|
|
|
{
|
|
|
|
|
AggregateFunction * func = static_cast<AggregateFunction *>(aggregate_functions[column_num]);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/** Может возникнуть исключение при нехватке памяти.
|
|
|
|
|
* Для того, чтобы потом всё правильно уничтожилось, "откатываем" часть созданных состояний.
|
|
|
|
|
* Код не очень удобный.
|
|
|
|
|
*/
|
|
|
|
|
func->create(aggregate_data + offsets_of_aggregate_states[column_num]);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
for (size_t rollback_j = 0; rollback_j < column_num; ++rollback_j)
|
|
|
|
|
func->destroy(aggregate_data + offsets_of_aggregate_states[rollback_j]);
|
|
|
|
|
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
2015-01-11 00:57:21 +00:00
|
|
|
|
template <typename Method, typename AggregateFunctionsList>
|
|
|
|
|
void NO_INLINE Aggregator::executeSpecialized(
|
2015-01-08 21:41:35 +00:00
|
|
|
|
Method & method,
|
|
|
|
|
Arena * aggregates_pool,
|
|
|
|
|
size_t rows,
|
|
|
|
|
ConstColumnPlainPtrs & key_columns,
|
|
|
|
|
AggregateColumns & aggregate_columns,
|
|
|
|
|
const Sizes & key_sizes,
|
|
|
|
|
StringRefs & keys,
|
|
|
|
|
bool no_more_keys,
|
|
|
|
|
AggregateDataPtr overflow_row) const
|
|
|
|
|
{
|
2015-01-11 00:57:21 +00:00
|
|
|
|
typename Method::State state;
|
|
|
|
|
state.init(key_columns);
|
|
|
|
|
|
|
|
|
|
if (!no_more_keys)
|
|
|
|
|
executeSpecializedCase<false, Method, AggregateFunctionsList>(
|
|
|
|
|
method, state, aggregates_pool, rows, key_columns, aggregate_columns, key_sizes, keys, overflow_row);
|
|
|
|
|
else
|
|
|
|
|
executeSpecializedCase<true, Method, AggregateFunctionsList>(
|
|
|
|
|
method, state, aggregates_pool, rows, key_columns, aggregate_columns, key_sizes, keys, overflow_row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wuninitialized"
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
2015-01-11 00:57:21 +00:00
|
|
|
|
template <bool no_more_keys, typename Method, typename AggregateFunctionsList>
|
|
|
|
|
void NO_INLINE Aggregator::executeSpecializedCase(
|
|
|
|
|
Method & method,
|
|
|
|
|
typename Method::State & state,
|
|
|
|
|
Arena * aggregates_pool,
|
|
|
|
|
size_t rows,
|
|
|
|
|
ConstColumnPlainPtrs & key_columns,
|
|
|
|
|
AggregateColumns & aggregate_columns,
|
|
|
|
|
const Sizes & key_sizes,
|
|
|
|
|
StringRefs & keys,
|
|
|
|
|
AggregateDataPtr overflow_row) const
|
|
|
|
|
{
|
2015-01-08 21:41:35 +00:00
|
|
|
|
/// Для всех строчек.
|
2015-01-11 00:57:21 +00:00
|
|
|
|
typename Method::iterator it;
|
|
|
|
|
typename Method::Key prev_key;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
for (size_t i = 0; i < rows; ++i)
|
|
|
|
|
{
|
|
|
|
|
bool inserted; /// Вставили новый ключ, или такой ключ уже был?
|
|
|
|
|
bool overflow = false; /// Новый ключ не поместился в хэш-таблицу из-за no_more_keys.
|
|
|
|
|
|
|
|
|
|
/// Получаем ключ для вставки в хэш-таблицу.
|
2015-12-03 03:18:42 +00:00
|
|
|
|
typename Method::Key key = state.getKey(key_columns, params.keys_size, i, key_sizes, keys, *aggregates_pool);
|
2015-01-11 00:57:21 +00:00
|
|
|
|
|
|
|
|
|
if (!no_more_keys) /// Вставляем.
|
|
|
|
|
{
|
|
|
|
|
/// Оптимизация для часто повторяющихся ключей.
|
2015-02-22 18:02:54 +00:00
|
|
|
|
if (!Method::no_consecutive_keys_optimization)
|
2015-01-11 00:57:21 +00:00
|
|
|
|
{
|
2015-02-22 18:02:54 +00:00
|
|
|
|
if (i != 0 && key == prev_key)
|
|
|
|
|
{
|
|
|
|
|
AggregateDataPtr value = Method::getAggregateData(it->second);
|
|
|
|
|
|
|
|
|
|
/// Добавляем значения в агрегатные функции.
|
|
|
|
|
AggregateFunctionsList::forEach(AggregateFunctionsUpdater(
|
2016-09-19 22:30:40 +00:00
|
|
|
|
aggregate_functions, offsets_of_aggregate_states, aggregate_columns, value, i, aggregates_pool));
|
2015-02-22 18:02:54 +00:00
|
|
|
|
|
|
|
|
|
method.onExistingKey(key, keys, *aggregates_pool);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
prev_key = key;
|
2015-01-11 00:57:21 +00:00
|
|
|
|
}
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
method.data.emplace(key, it, inserted);
|
2015-01-11 00:57:21 +00:00
|
|
|
|
}
|
2015-01-08 21:41:35 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/// Будем добавлять только если ключ уже есть.
|
|
|
|
|
inserted = false;
|
|
|
|
|
it = method.data.find(key);
|
|
|
|
|
if (method.data.end() == it)
|
|
|
|
|
overflow = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Если ключ не поместился, и данные не надо агрегировать в отдельную строку, то делать нечего.
|
2015-01-11 00:57:21 +00:00
|
|
|
|
if (no_more_keys && overflow && !overflow_row)
|
2015-02-22 16:09:16 +00:00
|
|
|
|
{
|
|
|
|
|
method.onExistingKey(key, keys, *aggregates_pool);
|
2015-01-08 21:41:35 +00:00
|
|
|
|
continue;
|
2015-02-22 16:09:16 +00:00
|
|
|
|
}
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
/// Если вставили новый ключ - инициализируем состояния агрегатных функций, и возможно, что-нибудь связанное с ключом.
|
|
|
|
|
if (inserted)
|
|
|
|
|
{
|
|
|
|
|
AggregateDataPtr & aggregate_data = Method::getAggregateData(it->second);
|
2015-03-07 01:10:14 +00:00
|
|
|
|
aggregate_data = nullptr;
|
2015-03-23 01:10:06 +00:00
|
|
|
|
|
2015-12-03 03:18:42 +00:00
|
|
|
|
method.onNewKey(*it, params.keys_size, i, keys, *aggregates_pool);
|
2015-03-23 01:10:06 +00:00
|
|
|
|
|
2015-03-07 01:10:14 +00:00
|
|
|
|
AggregateDataPtr place = aggregates_pool->alloc(total_size_of_aggregate_states);
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
AggregateFunctionsList::forEach(AggregateFunctionsCreator(
|
2015-03-07 01:10:14 +00:00
|
|
|
|
aggregate_functions, offsets_of_aggregate_states, aggregate_columns, place));
|
|
|
|
|
|
|
|
|
|
aggregate_data = place;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
}
|
2015-02-22 16:09:16 +00:00
|
|
|
|
else
|
|
|
|
|
method.onExistingKey(key, keys, *aggregates_pool);
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
2015-01-11 00:57:21 +00:00
|
|
|
|
AggregateDataPtr value = (!no_more_keys || !overflow) ? Method::getAggregateData(it->second) : overflow_row;
|
2015-01-08 21:41:35 +00:00
|
|
|
|
|
|
|
|
|
/// Добавляем значения в агрегатные функции.
|
|
|
|
|
AggregateFunctionsList::forEach(AggregateFunctionsUpdater(
|
2016-09-19 22:30:40 +00:00
|
|
|
|
aggregate_functions, offsets_of_aggregate_states, aggregate_columns, value, i, aggregates_pool));
|
2015-01-08 21:41:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-11 00:57:21 +00:00
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
|
2015-01-13 03:03:45 +00:00
|
|
|
|
template <typename AggregateFunctionsList>
|
|
|
|
|
void NO_INLINE Aggregator::executeSpecializedWithoutKey(
|
|
|
|
|
AggregatedDataWithoutKey & res,
|
|
|
|
|
size_t rows,
|
2016-09-21 16:39:44 +00:00
|
|
|
|
AggregateColumns & aggregate_columns,
|
|
|
|
|
Arena * arena) const
|
2015-01-13 03:03:45 +00:00
|
|
|
|
{
|
|
|
|
|
/// Оптимизация в случае единственной агрегатной функции count.
|
2015-12-03 03:18:42 +00:00
|
|
|
|
AggregateFunctionCount * agg_count = params.aggregates_size == 1
|
2015-01-13 03:03:45 +00:00
|
|
|
|
? typeid_cast<AggregateFunctionCount *>(aggregate_functions[0])
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
if (agg_count)
|
|
|
|
|
agg_count->addDelta(res, rows);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < rows; ++i)
|
|
|
|
|
{
|
|
|
|
|
AggregateFunctionsList::forEach(AggregateFunctionsUpdater(
|
2016-09-21 16:39:44 +00:00
|
|
|
|
aggregate_functions, offsets_of_aggregate_states, aggregate_columns, res, i, arena));
|
2015-01-13 03:03:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-08 21:41:35 +00:00
|
|
|
|
}
|
2015-04-01 04:14:15 +00:00
|
|
|
|
|
|
|
|
|
|
2016-02-11 01:43:39 +00:00
|
|
|
|
/** Основной код компилируется с помощью gcc 5.
|
2015-04-01 04:14:15 +00:00
|
|
|
|
* Но SpecializedAggregator компилируется с помощью clang 3.6 в .so-файл.
|
|
|
|
|
* Это делается потому что gcc не удаётся заставить инлайнить функции,
|
|
|
|
|
* которые были девиртуализированы, в конкретном случае, и производительность получается ниже.
|
|
|
|
|
* А также clang проще распространять для выкладки на серверы.
|
|
|
|
|
*
|
2016-02-11 01:43:39 +00:00
|
|
|
|
* После перехода с gcc 4.8 и gnu++1x на gcc 4.9 и gnu++1y (а затем на gcc 5),
|
2015-04-01 04:14:15 +00:00
|
|
|
|
* при dlopen стала возникать ошибка: undefined symbol: __cxa_pure_virtual
|
|
|
|
|
*
|
|
|
|
|
* Скорее всего, это происходит из-за изменившейся версии этого символа:
|
|
|
|
|
* gcc создаёт в .so символ
|
|
|
|
|
* U __cxa_pure_virtual@@CXXABI_1.3
|
|
|
|
|
* а clang создаёт символ
|
|
|
|
|
* U __cxa_pure_virtual
|
|
|
|
|
*
|
|
|
|
|
* Но нам не принципиально, как будет реализована функция __cxa_pure_virtual,
|
|
|
|
|
* потому что она не вызывается при нормальной работе программы,
|
|
|
|
|
* а если вызывается - то программа и так гарантированно глючит.
|
|
|
|
|
*
|
|
|
|
|
* Поэтому, мы можем обойти проблему таким образом:
|
|
|
|
|
*/
|
|
|
|
|
extern "C" void __attribute__((__visibility__("default"), __noreturn__)) __cxa_pure_virtual() { abort(); };
|