mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 09:32:01 +00:00
Introduce one template level to argMin to recover some perf
This commit is contained in:
parent
1e00dec997
commit
e1bd77fa31
@ -24,8 +24,9 @@ private:
|
||||
SerializationPtr serialization;
|
||||
|
||||
public:
|
||||
explicit AggregateFunctionAny(const DataTypePtr & type)
|
||||
: IAggregateFunctionDataHelper<Data, AggregateFunctionAny<Data>>({type}, {}, type), serialization(type->getDefaultSerialization())
|
||||
explicit AggregateFunctionAny(const DataTypes & argument_types_)
|
||||
: IAggregateFunctionDataHelper<Data, AggregateFunctionAny<Data>>(argument_types_, {}, argument_types_[0])
|
||||
, serialization(this->result_type->getDefaultSerialization())
|
||||
{
|
||||
}
|
||||
|
||||
@ -179,7 +180,8 @@ public:
|
||||
AggregateFunctionPtr
|
||||
createAggregateFunctionAny(const std::string & name, const DataTypes & argument_types, const Array & parameters, const Settings * settings)
|
||||
{
|
||||
return AggregateFunctionPtr(createAggregateFunctionSingleValue<AggregateFunctionAny>(name, argument_types, parameters, settings));
|
||||
return AggregateFunctionPtr(
|
||||
createAggregateFunctionSingleValue<AggregateFunctionAny, /* unary */ true>(name, argument_types, parameters, settings));
|
||||
}
|
||||
|
||||
|
||||
@ -190,9 +192,9 @@ private:
|
||||
SerializationPtr serialization;
|
||||
|
||||
public:
|
||||
explicit AggregateFunctionAnyLast(const DataTypePtr & type)
|
||||
: IAggregateFunctionDataHelper<Data, AggregateFunctionAnyLast<Data>>({type}, {}, type)
|
||||
, serialization(type->getDefaultSerialization())
|
||||
explicit AggregateFunctionAnyLast(const DataTypes & argument_types_)
|
||||
: IAggregateFunctionDataHelper<Data, AggregateFunctionAnyLast<Data>>(argument_types_, {}, argument_types_[0])
|
||||
, serialization(this->result_type->getDefaultSerialization())
|
||||
{
|
||||
}
|
||||
|
||||
@ -348,7 +350,8 @@ public:
|
||||
AggregateFunctionPtr createAggregateFunctionAnyLast(
|
||||
const std::string & name, const DataTypes & argument_types, const Array & parameters, const Settings * settings)
|
||||
{
|
||||
return AggregateFunctionPtr(createAggregateFunctionSingleValue<AggregateFunctionAnyLast>(name, argument_types, parameters, settings));
|
||||
return AggregateFunctionPtr(
|
||||
createAggregateFunctionSingleValue<AggregateFunctionAnyLast, /* unary */ true>(name, argument_types, parameters, settings));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ struct AggregateFunctionAnyHeavyData
|
||||
using Self = AggregateFunctionAnyHeavyData;
|
||||
|
||||
private:
|
||||
SingleValueDataBase::memory_block v_data;
|
||||
SingleValueDataBaseMemoryBlock v_data;
|
||||
UInt64 counter = 0;
|
||||
|
||||
public:
|
||||
|
@ -28,7 +28,7 @@ struct AggregateFunctionSingleValueOrNullData
|
||||
using Self = AggregateFunctionSingleValueOrNullData;
|
||||
|
||||
private:
|
||||
SingleValueDataBase::memory_block v_data;
|
||||
SingleValueDataBaseMemoryBlock v_data;
|
||||
bool first_value = true;
|
||||
bool is_null = false;
|
||||
|
||||
|
@ -22,63 +22,56 @@ extern const int LOGICAL_ERROR;
|
||||
namespace
|
||||
{
|
||||
|
||||
template <class ValueType>
|
||||
struct AggregateFunctionArgMinMaxData
|
||||
{
|
||||
private:
|
||||
SingleValueDataBase::memory_block r_data;
|
||||
SingleValueDataBase::memory_block v_data;
|
||||
SingleValueDataBaseMemoryBlock result_data;
|
||||
ValueType value_data;
|
||||
|
||||
public:
|
||||
SingleValueDataBase & result() { return r_data.get(); }
|
||||
const SingleValueDataBase & result() const { return r_data.get(); }
|
||||
SingleValueDataBase & value() { return v_data.get(); }
|
||||
const SingleValueDataBase & value() const { return v_data.get(); }
|
||||
SingleValueDataBase & result() { return result_data.get(); }
|
||||
const SingleValueDataBase & result() const { return result_data.get(); }
|
||||
ValueType & value() { return value_data; }
|
||||
const ValueType & value() const { return value_data; }
|
||||
|
||||
[[noreturn]] explicit AggregateFunctionArgMinMaxData()
|
||||
{
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "AggregateFunctionArgMinMaxData initialized empty");
|
||||
}
|
||||
|
||||
explicit AggregateFunctionArgMinMaxData(TypeIndex result_type, TypeIndex value_type)
|
||||
explicit AggregateFunctionArgMinMaxData(TypeIndex result_type) : value_data()
|
||||
{
|
||||
generateSingleValueFromTypeIndex(result_type, r_data);
|
||||
generateSingleValueFromTypeIndex(value_type, v_data);
|
||||
generateSingleValueFromTypeIndex(result_type, result_data);
|
||||
}
|
||||
|
||||
~AggregateFunctionArgMinMaxData()
|
||||
{
|
||||
result().~SingleValueDataBase();
|
||||
value().~SingleValueDataBase();
|
||||
}
|
||||
~AggregateFunctionArgMinMaxData() { result().~SingleValueDataBase(); }
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(AggregateFunctionArgMinMaxData) == 2 * SingleValueDataBase::MAX_STORAGE_SIZE,
|
||||
sizeof(AggregateFunctionArgMinMaxData<Int8>) <= 2 * SingleValueDataBase::MAX_STORAGE_SIZE,
|
||||
"Incorrect size of AggregateFunctionArgMinMaxData struct");
|
||||
|
||||
/// Returns the first arg value found for the minimum/maximum value. Example: argMin(arg, value).
|
||||
template <bool isMin>
|
||||
template <typename ValueData, bool isMin>
|
||||
class AggregateFunctionArgMinMax final
|
||||
: public IAggregateFunctionDataHelper<AggregateFunctionArgMinMaxData, AggregateFunctionArgMinMax<isMin>>
|
||||
: public IAggregateFunctionDataHelper<AggregateFunctionArgMinMaxData<ValueData>, AggregateFunctionArgMinMax<ValueData, isMin>>
|
||||
{
|
||||
private:
|
||||
const DataTypePtr & type_val;
|
||||
const SerializationPtr serialization_res;
|
||||
const SerializationPtr serialization_val;
|
||||
|
||||
const TypeIndex result_type_index;
|
||||
const TypeIndex value_type_index;
|
||||
|
||||
using Base = IAggregateFunctionDataHelper<AggregateFunctionArgMinMaxData, AggregateFunctionArgMinMax<isMin>>;
|
||||
using Base = IAggregateFunctionDataHelper<AggregateFunctionArgMinMaxData<ValueData>, AggregateFunctionArgMinMax<ValueData, isMin>>;
|
||||
|
||||
public:
|
||||
AggregateFunctionArgMinMax(const DataTypePtr & type_res_, const DataTypePtr & type_val_)
|
||||
: Base({type_res_, type_val_}, {}, type_res_)
|
||||
AggregateFunctionArgMinMax(const DataTypes & argument_types_)
|
||||
: Base(argument_types_, {}, argument_types_[0])
|
||||
, type_val(this->argument_types[1])
|
||||
, serialization_res(type_res_->getDefaultSerialization())
|
||||
, serialization_val(type_val->getDefaultSerialization())
|
||||
, result_type_index(WhichDataType(type_res_).idx)
|
||||
, value_type_index(WhichDataType(type_val_).idx)
|
||||
, serialization_res(this->argument_types[0]->getDefaultSerialization())
|
||||
, serialization_val(this->argument_types[1]->getDefaultSerialization())
|
||||
, result_type_index(WhichDataType(this->argument_types[0]).idx)
|
||||
{
|
||||
if (!type_val->isComparable())
|
||||
throw Exception(
|
||||
@ -90,7 +83,7 @@ public:
|
||||
|
||||
void create(AggregateDataPtr __restrict place) const override /// NOLINT
|
||||
{
|
||||
new (place) AggregateFunctionArgMinMaxData(result_type_index, value_type_index);
|
||||
new (place) AggregateFunctionArgMinMaxData<ValueData>(result_type_index);
|
||||
}
|
||||
|
||||
String getName() const override
|
||||
@ -214,7 +207,7 @@ public:
|
||||
|
||||
bool allocatesMemoryInArena() const override
|
||||
{
|
||||
return singleValueTypeAllocatesMemoryInArena(result_type_index) || singleValueTypeAllocatesMemoryInArena(value_type_index);
|
||||
return singleValueTypeAllocatesMemoryInArena(result_type_index) || ValueData::allocatesMemoryInArena();
|
||||
}
|
||||
|
||||
void insertResultInto(AggregateDataPtr __restrict place, IColumn & to, Arena *) const override
|
||||
@ -224,16 +217,11 @@ public:
|
||||
};
|
||||
|
||||
template <bool isMin>
|
||||
AggregateFunctionPtr
|
||||
createAggregateFunctionArgMinMax(const std::string & name, const DataTypes & argument_types, const Array & parameters, const Settings *)
|
||||
AggregateFunctionPtr createAggregateFunctionArgMinMax(
|
||||
const std::string & name, const DataTypes & argument_types, const Array & parameters, const Settings * settings)
|
||||
{
|
||||
assertNoParameters(name, parameters);
|
||||
assertBinary(name, argument_types);
|
||||
|
||||
const DataTypePtr & res_type = argument_types[0];
|
||||
const DataTypePtr & val_type = argument_types[1];
|
||||
|
||||
return AggregateFunctionPtr(new AggregateFunctionArgMinMax<isMin>(res_type, val_type));
|
||||
return AggregateFunctionPtr(createAggregateFunctionSingleValue<AggregateFunctionArgMinMax, /* unary */ false, isMin>(
|
||||
name, argument_types, parameters, settings));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,15 +25,15 @@ private:
|
||||
SerializationPtr serialization;
|
||||
|
||||
public:
|
||||
explicit AggregateFunctionMinMax(const DataTypePtr & type)
|
||||
: IAggregateFunctionDataHelper<Data, AggregateFunctionMinMax<Data, isMin>>({type}, {}, type)
|
||||
, serialization(type->getDefaultSerialization())
|
||||
explicit AggregateFunctionMinMax(const DataTypes & argument_types_)
|
||||
: IAggregateFunctionDataHelper<Data, AggregateFunctionMinMax<Data, isMin>>(argument_types_, {}, argument_types_[0])
|
||||
, serialization(this->result_type->getDefaultSerialization())
|
||||
{
|
||||
if (!type->isComparable())
|
||||
if (!this->result_type->isComparable())
|
||||
throw Exception(
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of argument of aggregate function {} because the values of that data type are not comparable",
|
||||
type->getName(),
|
||||
this->result_type->getName(),
|
||||
getName());
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ AggregateFunctionPtr createAggregateFunctionMinMax(
|
||||
const std::string & name, const DataTypes & argument_types, const Array & parameters, const Settings * settings)
|
||||
{
|
||||
return AggregateFunctionPtr(
|
||||
createAggregateFunctionSingleValue<AggregateFunctionMinMax, isMin>(name, argument_types, parameters, settings));
|
||||
createAggregateFunctionSingleValue<AggregateFunctionMinMax, /* unary */ true, isMin>(name, argument_types, parameters, settings));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace
|
||||
struct AggregateFunctionCombinatorArgMinArgMaxData
|
||||
{
|
||||
private:
|
||||
SingleValueDataBase::memory_block v_data;
|
||||
SingleValueDataBaseMemoryBlock v_data;
|
||||
|
||||
public:
|
||||
explicit AggregateFunctionCombinatorArgMinArgMaxData(TypeIndex value_type) { generateSingleValueFromTypeIndex(value_type, v_data); }
|
||||
@ -93,7 +93,7 @@ public:
|
||||
|
||||
size_t sizeOfData() const override { return key_offset + sizeof(Key); }
|
||||
|
||||
size_t alignOfData() const override { return std::max(nested_function->alignOfData(), alignof(SingleValueDataBase::memory_block)); }
|
||||
size_t alignOfData() const override { return std::max(nested_function->alignOfData(), alignof(SingleValueDataBaseMemoryBlock)); }
|
||||
|
||||
void create(AggregateDataPtr __restrict place) const override
|
||||
{
|
||||
|
@ -1380,13 +1380,14 @@ bool SingleValueDataGeneric::setIfGreater(const SingleValueDataBase & other, Are
|
||||
return false;
|
||||
}
|
||||
|
||||
void generateSingleValueFromTypeIndex(TypeIndex idx, SingleValueDataBase::memory_block & data)
|
||||
void generateSingleValueFromTypeIndex(TypeIndex idx, SingleValueDataBaseMemoryBlock & data)
|
||||
{
|
||||
#define DISPATCH(TYPE) \
|
||||
if (idx == TypeIndex::TYPE) \
|
||||
{ \
|
||||
static_assert(sizeof(SingleValueDataNumeric<TYPE>) <= SingleValueDataBase::MAX_STORAGE_SIZE); \
|
||||
new (data.memory) SingleValueDataNumeric<TYPE>; \
|
||||
static_assert(sizeof(SingleValueDataNumeric<TYPE>) <= sizeof(SingleValueDataBaseMemoryBlock::memory)); \
|
||||
static_assert(alignof(SingleValueDataNumeric<TYPE>) <= alignof(SingleValueDataBaseMemoryBlock)); \
|
||||
new (&data.memory) SingleValueDataNumeric<TYPE>; \
|
||||
return; \
|
||||
}
|
||||
|
||||
@ -1395,24 +1396,28 @@ void generateSingleValueFromTypeIndex(TypeIndex idx, SingleValueDataBase::memory
|
||||
|
||||
if (idx == TypeIndex::Date)
|
||||
{
|
||||
static_assert(sizeof(SingleValueDataFixed<DataTypeDate::FieldType>) <= SingleValueDataBase::MAX_STORAGE_SIZE);
|
||||
new (data.memory) SingleValueDataNumeric<DataTypeDate::FieldType>;
|
||||
static_assert(sizeof(SingleValueDataNumeric<DataTypeDate::FieldType>) <= sizeof(SingleValueDataBaseMemoryBlock::memory));
|
||||
static_assert(alignof(SingleValueDataNumeric<DataTypeDate::FieldType>) <= alignof(SingleValueDataBaseMemoryBlock));
|
||||
new (&data.memory) SingleValueDataNumeric<DataTypeDate::FieldType>;
|
||||
return;
|
||||
}
|
||||
if (idx == TypeIndex::DateTime)
|
||||
{
|
||||
static_assert(sizeof(SingleValueDataFixed<DataTypeDateTime::FieldType>) <= SingleValueDataBase::MAX_STORAGE_SIZE);
|
||||
new (data.memory) SingleValueDataNumeric<DataTypeDateTime::FieldType>;
|
||||
static_assert(sizeof(SingleValueDataNumeric<DataTypeDateTime::FieldType>) <= sizeof(SingleValueDataBaseMemoryBlock::memory));
|
||||
static_assert(alignof(SingleValueDataNumeric<DataTypeDateTime::FieldType>) <= alignof(SingleValueDataBaseMemoryBlock));
|
||||
new (&data.memory) SingleValueDataNumeric<DataTypeDateTime::FieldType>;
|
||||
return;
|
||||
}
|
||||
if (idx == TypeIndex::String)
|
||||
{
|
||||
static_assert(sizeof(SingleValueDataString) <= SingleValueDataBase::MAX_STORAGE_SIZE);
|
||||
new (data.memory) SingleValueDataString;
|
||||
static_assert(sizeof(SingleValueDataString) <= sizeof(SingleValueDataBaseMemoryBlock::memory));
|
||||
static_assert(alignof(SingleValueDataString) <= alignof(SingleValueDataBaseMemoryBlock));
|
||||
new (&data.memory) SingleValueDataString;
|
||||
return;
|
||||
}
|
||||
static_assert(sizeof(SingleValueDataGeneric) <= SingleValueDataBase::MAX_STORAGE_SIZE);
|
||||
new (data.memory) SingleValueDataGeneric;
|
||||
static_assert(sizeof(SingleValueDataGeneric) <= sizeof(SingleValueDataBaseMemoryBlock::memory));
|
||||
static_assert(alignof(SingleValueDataGeneric) <= alignof(SingleValueDataBaseMemoryBlock));
|
||||
new (&data.memory) SingleValueDataGeneric;
|
||||
}
|
||||
|
||||
bool singleValueTypeAllocatesMemoryInArena(TypeIndex idx)
|
||||
|
@ -21,19 +21,9 @@ struct SingleValueDataBase
|
||||
static constexpr int nan_direction_hint = 1;
|
||||
/// Any subclass (numeric, string, generic) must be smaller than MAX_STORAGE_SIZE
|
||||
/// We use this knowledge to create composite data classes that use them directly by reserving a 'memory_block'
|
||||
/// For example argMin holds 2 of these objects
|
||||
/// For example argMin holds 1 of these (for the result), while keeping a template for the value
|
||||
static constexpr UInt32 MAX_STORAGE_SIZE = 64;
|
||||
|
||||
/// Helper to allocate enough memory to store any derived class and subclasses will be misaligned
|
||||
/// alignas is necessary as otherwise alignof(memory_block) == 1
|
||||
struct alignas(MAX_STORAGE_SIZE) memory_block
|
||||
{
|
||||
char memory[SingleValueDataBase::MAX_STORAGE_SIZE];
|
||||
SingleValueDataBase & get() { return *reinterpret_cast<SingleValueDataBase *>(memory); }
|
||||
const SingleValueDataBase & get() const { return *reinterpret_cast<const SingleValueDataBase *>(memory); }
|
||||
};
|
||||
static_assert(alignof(memory_block) == SingleValueDataBase::MAX_STORAGE_SIZE);
|
||||
|
||||
virtual ~SingleValueDataBase() { }
|
||||
virtual bool has() const = 0;
|
||||
virtual void insertResultInto(IColumn &) const = 0;
|
||||
@ -353,37 +343,52 @@ public:
|
||||
static_assert(sizeof(SingleValueDataGeneric) <= SingleValueDataBase::MAX_STORAGE_SIZE, "Incorrect size of SingleValueDataGeneric struct");
|
||||
|
||||
/// min, max, any, anyLast, anyHeavy, etc...
|
||||
template <template <typename, bool...> class AggregateFunctionTemplate, bool... isMin>
|
||||
template <template <typename, bool...> class AggregateFunctionTemplate, bool unary, bool... isMin>
|
||||
static IAggregateFunction *
|
||||
createAggregateFunctionSingleValue(const String & name, const DataTypes & argument_types, const Array & parameters, const Settings *)
|
||||
{
|
||||
assertNoParameters(name, parameters);
|
||||
if constexpr (unary)
|
||||
assertUnary(name, argument_types);
|
||||
else
|
||||
assertBinary(name, argument_types);
|
||||
|
||||
const DataTypePtr & argument_type = argument_types[0];
|
||||
WhichDataType which(argument_type);
|
||||
const DataTypePtr & value_type = unary ? argument_types[0] : argument_types[1];
|
||||
WhichDataType which(value_type);
|
||||
#define DISPATCH(TYPE) \
|
||||
if (which.idx == TypeIndex::TYPE) \
|
||||
return new AggregateFunctionTemplate<SingleValueDataFixed<TYPE>, isMin...>(argument_type); /// NOLINT
|
||||
return new AggregateFunctionTemplate<SingleValueDataFixed<TYPE>, isMin...>(argument_types); /// NOLINT
|
||||
FOR_SINGLE_VALUE_NUMERIC_TYPES(DISPATCH)
|
||||
#undef DISPATCH
|
||||
|
||||
if (which.idx == TypeIndex::Date)
|
||||
return new AggregateFunctionTemplate<SingleValueDataFixed<DataTypeDate::FieldType>, isMin...>(argument_type);
|
||||
return new AggregateFunctionTemplate<SingleValueDataFixed<DataTypeDate::FieldType>, isMin...>(argument_types);
|
||||
if (which.idx == TypeIndex::DateTime)
|
||||
return new AggregateFunctionTemplate<SingleValueDataFixed<DataTypeDateTime::FieldType>, isMin...>(argument_type);
|
||||
return new AggregateFunctionTemplate<SingleValueDataFixed<DataTypeDateTime::FieldType>, isMin...>(argument_types);
|
||||
if (which.idx == TypeIndex::String)
|
||||
return new AggregateFunctionTemplate<SingleValueDataString, isMin...>(argument_type);
|
||||
return new AggregateFunctionTemplate<SingleValueDataString, isMin...>(argument_types);
|
||||
|
||||
return new AggregateFunctionTemplate<SingleValueDataGeneric, isMin...>(argument_type);
|
||||
return new AggregateFunctionTemplate<SingleValueDataGeneric, isMin...>(argument_types);
|
||||
}
|
||||
|
||||
/// Helper when you want to build SingleValueNumeric
|
||||
void generateSingleValueNumericFromTypeIndex(TypeIndex idx, SingleValueDataBase::memory_block & data);
|
||||
/// Helper to allocate enough memory to store any derived class
|
||||
struct SingleValueDataBaseMemoryBlock
|
||||
{
|
||||
std::aligned_union_t<
|
||||
SingleValueDataBase::MAX_STORAGE_SIZE,
|
||||
SingleValueDataNumeric<Decimal256>, /// We check all types in generateSingleValueFromTypeIndex
|
||||
SingleValueDataString,
|
||||
SingleValueDataGeneric>
|
||||
memory;
|
||||
SingleValueDataBase & get() { return *reinterpret_cast<SingleValueDataBase *>(&memory); }
|
||||
const SingleValueDataBase & get() const { return *reinterpret_cast<const SingleValueDataBase *>(&memory); }
|
||||
};
|
||||
|
||||
static_assert(alignof(SingleValueDataBaseMemoryBlock) == 8);
|
||||
|
||||
/// For Data classes that want to compose on top of SingleValueDataBase values, like argMax or singleValueOrNull
|
||||
/// It will build the object based on the type idx on the memory block provided
|
||||
void generateSingleValueFromTypeIndex(TypeIndex idx, SingleValueDataBase::memory_block & data);
|
||||
void generateSingleValueFromTypeIndex(TypeIndex idx, SingleValueDataBaseMemoryBlock & data);
|
||||
|
||||
bool singleValueTypeAllocatesMemoryInArena(TypeIndex idx);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
UInt64 1 8
|
||||
UInt64 10 80
|
||||
UInt64 1000 8000
|
||||
AggregateFunction(argMax, String, DateTime) 1 136
|
||||
AggregateFunction(argMax, String, DateTime) 10 1360
|
||||
AggregateFunction(argMax, String, DateTime) 1000 136000
|
||||
AggregateFunction(argMax, String, DateTime) 1 80
|
||||
AggregateFunction(argMax, String, DateTime) 10 800
|
||||
AggregateFunction(argMax, String, DateTime) 1000 80000
|
||||
|
@ -42,8 +42,10 @@ ORDER BY event_time_microseconds;
|
||||
-- 1 * 8 + AggregateFunction(argMax, String, DateTime)
|
||||
--
|
||||
-- Size of AggregateFunction(argMax, String, DateTime):
|
||||
-- 2 * MAX(sizeOf(SingleValueDataFixed<T>), sizeOf(SingleValueDataString), sizeOf(SingleValueDataGeneric))
|
||||
-- Which is the same as 2 * SingleValueDataBase::MAX_STORAGE_SIZE (64)
|
||||
-- 1 Base class + 1 specific/value class:
|
||||
-- Base class: MAX(sizeOf(SingleValueDataFixed<T>), sizeOf(SingleValueDataString), sizeOf(SingleValueDataGeneric)) = 64
|
||||
-- Specific class: SingleValueDataFixed(DateTime) = 4 + 1. With padding = 8
|
||||
-- Total: 8 + 64 + 8 = 80
|
||||
--
|
||||
-- ColumnAggregateFunction total: 8 + 2 * 64 = 136
|
||||
SELECT 'AggregateFunction(argMax, String, DateTime)',
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- When we use SingleValueDataBase::memory_block we must ensure we call the class destructor on destroy
|
||||
-- When we use SingleValueDataBaseMemoryBlock we must ensure we call the class destructor on destroy
|
||||
|
||||
Select argMax((number, number), (number, number)) FROM numbers(100000) format Null;
|
||||
Select argMin((number, number), (number, number)) FROM numbers(100000) format Null;
|
||||
|
Loading…
Reference in New Issue
Block a user