WIP - Made code much more compact

This commit is contained in:
akazz 2019-07-17 19:10:37 +03:00
parent f75585cd46
commit 6f9d87f787

View File

@ -91,7 +91,101 @@ void convertColumnToUInt8(const IColumn * column, UInt8Container & res)
} }
/// TODO: Add a good comment here about what this is template <class Op, typename Func>
static bool extractConstColumns(ColumnRawPtrs & in, UInt8 & res, Func && func)
{
bool has_res = false;
for (int i = static_cast<int>(in.size()) - 1; i >= 0; --i)
{
if (!in[i]->isColumnConst())
continue;
UInt8 x = func((*in[i])[0]);
if (has_res)
{
res = Op::apply(res, x);
}
else
{
res = x;
has_res = true;
}
in.erase(in.begin() + i);
}
return has_res;
}
template <class Op>
inline bool extractConstColumns(ColumnRawPtrs & in, UInt8 & res)
{
return extractConstColumns<Op>(
in, res,
[](const Field & value)
{
return !value.isNull() && applyVisitor(FieldVisitorConvertToNumber<bool>(), value);
}
);
}
template <class Op>
inline bool extractConstColumnsTernary(ColumnRawPtrs & in, UInt8 & res_3v)
{
return extractConstColumns<Op>(
in, res_3v,
[](const Field & value)
{
return value.isNull()
? Ternary::makeValue(false, true)
: Ternary::makeValue(applyVisitor(FieldVisitorConvertToNumber<bool>(), value));
}
);
}
template <typename Op, size_t N>
class AssociativeApplierImpl
{
using ResultValueType = typename Op::ResultType;
public:
/// Remembers the last N columns from `in`.
AssociativeApplierImpl(const UInt8ColumnPtrs & in)
: vec(in[in.size() - N]->getData()), next(in) {}
/// Returns a combination of values in the i-th row of all columns stored in the constructor.
inline ResultValueType apply(const size_t i) const
{
const auto & a = vec[i];
if constexpr (Op::isSaturable())
return Op::isSaturatedValue(a) ? a : Op::apply(a, next.apply(i));
else
return Op::apply(a, next.apply(i));
}
private:
const UInt8Container & vec;
const AssociativeApplierImpl<Op, N - 1> next;
};
template <typename Op>
class AssociativeApplierImpl<Op, 1>
{
using ResultValueType = typename Op::ResultType;
public:
AssociativeApplierImpl(const UInt8ColumnPtrs & in)
: vec(in[in.size() - 1]->getData()) {}
inline ResultValueType apply(const size_t i) const { return vec[i]; }
private:
const UInt8Container & vec;
};
/// A helper class used by AssociativeGenericApplierImpl
/// Allows for on-the-fly conversion of any data type into intermediate ternary representation
using ValueGetter = std::function<Ternary::ResultType (size_t)>; using ValueGetter = std::function<Ternary::ResultType (size_t)>;
template <typename ... Types> template <typename ... Types>
@ -133,78 +227,9 @@ struct ValueGetterBuilderImpl<>
using ValueGetterBuilder = using ValueGetterBuilder =
ValueGetterBuilderImpl<UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64>; ValueGetterBuilderImpl<UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64>;
/// This class together with helper class ValueGetterBuilder can be used with columns of arbitrary data type
template <typename Op, size_t N> /// Allows for on-the-fly conversion of any type of data into intermediate ternary representation
class AssociativeApplierImpl /// and eliminates the need to materialize data columns in intermediate representation
{
using ResultValueType = typename Op::ResultType;
public:
/// Remembers the last N columns from `in`.
AssociativeApplierImpl(const UInt8ColumnPtrs & in)
: vec(in[in.size() - N]->getData()), next(in) {}
/// Returns a combination of values in the i-th row of all columns stored in the constructor.
inline ResultValueType apply(const ResultValueType a, const size_t i) const
{
if constexpr (Op::isSaturable())
return Op::isSaturatedValue(a) ? a : Op::apply(a, next.apply(vec[i], i));
else
return Op::apply(a, next.apply(vec[i], i));
}
private:
const UInt8Container & vec;
const AssociativeApplierImpl<Op, N - 1> next;
};
template <typename Op>
class AssociativeApplierImpl<Op, 1>
{
using ResultValueType = typename Op::ResultType;
public:
AssociativeApplierImpl(const UInt8ColumnPtrs & in)
: vec(in[in.size() - 1]->getData()) {}
inline ResultValueType apply(const ResultValueType a, const size_t i) const
{
return Op::apply(a, vec[i]);
}
private:
const UInt8Container & vec;
};
template <class Op>
static bool extractConstColumns(ColumnRawPtrs & in, UInt8 & res)
{
bool has_res = false;
for (int i = static_cast<int>(in.size()) - 1; i >= 0; --i)
{
if (!in[i]->isColumnConst())
continue;
Field value = (*in[i])[0];
UInt8 x = !value.isNull() && applyVisitor(FieldVisitorConvertToNumber<bool>(), value);
if (has_res)
{
res = Op::apply(res, x);
}
else
{
res = x;
has_res = true;
}
in.erase(in.begin() + i);
}
return has_res;
}
template <typename Op, size_t N> template <typename Op, size_t N>
class AssociativeGenericApplierImpl class AssociativeGenericApplierImpl
{ {
@ -216,17 +241,13 @@ public:
: val_getter{ValueGetterBuilder::build(in[in.size() - N])}, next{in} {} : val_getter{ValueGetterBuilder::build(in[in.size() - N])}, next{in} {}
/// Returns a combination of values in the i-th row of all columns stored in the constructor. /// Returns a combination of values in the i-th row of all columns stored in the constructor.
inline ResultValueType apply(const ResultValueType a, const size_t i) const inline ResultValueType apply(const size_t i) const
{ {
const auto a = val_getter(i);
if constexpr (Op::isSaturable()) if constexpr (Op::isSaturable())
return Op::isSaturatedValue(a) ? a : Op::apply(a, next.apply(i)); return Op::isSaturatedValue(a) ? a : Op::apply(a, next.apply(i));
else else
return Op::apply(a, next.apply(i)); return Op::apply(a, next.apply(i));
}
inline ResultValueType apply(const size_t i) const
{
return apply(val_getter(i), i);
} }
private: private:
@ -235,27 +256,6 @@ private:
}; };
template <typename Op>
class AssociativeGenericApplierImpl<Op, 2>
{
using ResultValueType = typename Op::ResultType;
public:
/// Remembers the last N columns from `in`.
AssociativeGenericApplierImpl(const ColumnRawPtrs & in)
: val_getter_L{ValueGetterBuilder::build(in[in.size() - 2])}
, val_getter_R{ValueGetterBuilder::build(in[in.size() - 1])} {}
inline ResultValueType apply(const size_t i) const
{
return Op::apply(val_getter_L(i), val_getter_R(i));
}
private:
const ValueGetter val_getter_L;
const ValueGetter val_getter_R;
};
template <typename Op> template <typename Op>
class AssociativeGenericApplierImpl<Op, 1> class AssociativeGenericApplierImpl<Op, 1>
{ {
@ -273,40 +273,8 @@ private:
}; };
template <class Op>
bool extractConstColumnsTernary(ColumnRawPtrs & in, UInt8 & res_3v)
{
bool has_res = false;
for (int i = static_cast<int>(in.size()) - 1; i >= 0; --i)
{
if (!in[i]->isColumnConst())
continue;
const auto field_value = (*in[i])[0];
UInt8 value_3v = field_value.isNull()
? Ternary::makeValue(false, true)
: Ternary::makeValue(applyVisitor(FieldVisitorConvertToNumber<bool>(), field_value));
if (has_res)
{
res_3v = Op::apply(res_3v, value_3v);
}
else
{
res_3v = value_3v;
has_res = true;
}
in.erase(in.begin() + i);
}
return has_res;
}
template < template <
template <typename, size_t> typename OperationApplierImpl, typename Op, template <typename, size_t> typename OperationApplierImpl, typename Op, size_t N>
size_t N, bool use_result_as_input>
struct OperationApplier struct OperationApplier
{ {
template <typename Columns, typename Result> template <typename Columns, typename Result>
@ -314,17 +282,14 @@ struct OperationApplier
{ {
if (N > in.size()) if (N > in.size())
{ {
OperationApplier<OperationApplierImpl, Op, N - 1, use_result_as_input>::run(in, result); OperationApplier<OperationApplierImpl, Op, N - 1>::run(in, result);
return; return;
} }
const OperationApplierImpl<Op, N> operationApplierImpl(in); const OperationApplierImpl<Op, N> operationApplierImpl(in);
size_t i = 0; size_t i = 0;
for (auto & res : result) for (auto & res : result)
if constexpr (use_result_as_input) res = operationApplierImpl.apply(i++);
res = operationApplierImpl.apply(res, i++);
else
res = operationApplierImpl.apply(i++);
in.erase(in.end() - N, in.end()); in.erase(in.end() - N, in.end());
} }
@ -332,20 +297,7 @@ struct OperationApplier
template < template <
template <typename, size_t> typename OperationApplierImpl, typename Op> template <typename, size_t> typename OperationApplierImpl, typename Op>
struct OperationApplier<OperationApplierImpl, Op, 0, true> struct OperationApplier<OperationApplierImpl, Op, 1>
{
template <typename Columns, typename Result>
static void NO_INLINE run(Columns &, Result &)
{
throw Exception(
"AssociativeOperationImpl::execute(...): not enough arguments to run this method",
ErrorCodes::LOGICAL_ERROR);
}
};
template <
template <typename, size_t> typename OperationApplierImpl, typename Op>
struct OperationApplier<OperationApplierImpl, Op, 1, false>
{ {
template <typename Columns, typename Result> template <typename Columns, typename Result>
static void NO_INLINE run(Columns &, Result &) static void NO_INLINE run(Columns &, Result &)
@ -376,14 +328,12 @@ static void executeForTernaryLogicImpl(ColumnRawPtrs arguments, ColumnWithTypeAn
const auto result_column = ColumnUInt8::create(input_rows_count); const auto result_column = ColumnUInt8::create(input_rows_count);
/// Efficiently combine all the columns of the correct type. /// Combining 10 columns per pass is the fastest for large block sizes.
while (arguments.size() > 1) /// For small block sizes - more columns is faster.
{
/// Combining 10 columns per pass is the fastest for large block sizes. /// Apply function to packs of size 10
/// For small block sizes - more columns is faster. /// TODO: Need a better comment
OperationApplier<AssociativeGenericApplierImpl, Op, 10, false>::run(arguments, result_column->getData()); OperationApplier<AssociativeGenericApplierImpl, Op, 10>::run(arguments, result_column->getData());
arguments.insert(arguments.cbegin(), result_column.get());
}
result_info.column = convertFromTernaryData(result_column->getData(), result_info.type->isNullable()); result_info.column = convertFromTernaryData(result_column->getData(), result_info.type->isNullable());
} }
@ -440,13 +390,11 @@ static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & re
} }
/// Effeciently combine all the columns of the correct type. /// Effeciently combine all the columns of the correct type.
while (uint8_args.size() > 1) /// With a large block size, combining 10 columns per pass is the fastest.
{ /// When small - more, is faster.
/// With a large block size, combining 10 columns per pass is the fastest. OperationApplier<AssociativeApplierImpl, Op, 10>::run(uint8_args, vec_res);
/// When small - more, is faster. // while (uint8_args.size() > 0)
OperationApplier<AssociativeApplierImpl, Op, 10, true>::run(uint8_args, vec_res); // OperationApplier<AssociativeApplierImpl, Op, 10>::run(uint8_args, vec_res);
uint8_args.push_back(col_res.get());
}
/// This is possible if there is exactly one non-constant among the arguments, and it is of type UInt8. /// This is possible if there is exactly one non-constant among the arguments, and it is of type UInt8.
if (uint8_args[0] != col_res.get()) if (uint8_args[0] != col_res.get())