mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-10-18 14:30:49 +00:00
Delete awful template PerformanceAdaptro and add simple ImplementationSelector instead
This commit is contained in:
parent
ea1285328b
commit
c524642d24
@ -143,26 +143,39 @@ private:
|
|||||||
) // DECLARE_MULTITARGET_CODE
|
) // DECLARE_MULTITARGET_CODE
|
||||||
|
|
||||||
template <typename Name>
|
template <typename Name>
|
||||||
class FunctionStartsEndsWith
|
class FunctionStartsEndsWith : public TargetSpecific::Default::FunctionStartsEndsWith<Name>
|
||||||
: public FunctionPerformanceAdaptor<TargetSpecific::Default::FunctionStartsEndsWith<Name>>
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FunctionStartsEndsWith(const Context & context_)
|
FunctionStartsEndsWith(const Context & context) : selector(context)
|
||||||
: FunctionPerformanceAdaptor<TargetSpecific::Default::FunctionStartsEndsWith<Name>>(context_)
|
|
||||||
{
|
{
|
||||||
|
selector.registerImplementation<TargetArch::Default,
|
||||||
|
TargetSpecific::Default::FunctionStartsEndsWith<Name>>();
|
||||||
|
|
||||||
if constexpr (UseMultitargetCode)
|
if constexpr (UseMultitargetCode)
|
||||||
{
|
{
|
||||||
this->template registerImplementation<TargetSpecific::SSE42::FunctionStartsEndsWith<Name>> (TargetArch::SSE42);
|
selector.registerImplementation<TargetArch::SSE42,
|
||||||
this->template registerImplementation<TargetSpecific::AVX::FunctionStartsEndsWith<Name>> (TargetArch::AVX);
|
TargetSpecific::SSE42::FunctionStartsEndsWith<Name>>();
|
||||||
this->template registerImplementation<TargetSpecific::AVX2::FunctionStartsEndsWith<Name>> (TargetArch::AVX2);
|
selector.registerImplementation<TargetArch::AVX,
|
||||||
this->template registerImplementation<TargetSpecific::AVX512F::FunctionStartsEndsWith<Name>>(TargetArch::AVX512F);
|
TargetSpecific::AVX::FunctionStartsEndsWith<Name>>();
|
||||||
|
selector.registerImplementation<TargetArch::AVX2,
|
||||||
|
TargetSpecific::AVX2::FunctionStartsEndsWith<Name>>();
|
||||||
|
selector.registerImplementation<TargetArch::AVX512F,
|
||||||
|
TargetSpecific::AVX512F::FunctionStartsEndsWith<Name>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
||||||
|
{
|
||||||
|
selector.selectAndExecute(block, arguments, result, input_rows_count);
|
||||||
|
}
|
||||||
|
|
||||||
static FunctionPtr create(const Context & context)
|
static FunctionPtr create(const Context & context)
|
||||||
{
|
{
|
||||||
return std::make_shared<FunctionStartsEndsWith<Name>>(context);
|
return std::make_shared<FunctionStartsEndsWith<Name>>(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImplementationSelector<IFunction> selector;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -99,28 +99,44 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename ToType, typename Name>
|
template <typename ToType, typename Name>
|
||||||
class FunctionRandom : public FunctionPerformanceAdaptor<FunctionRandomImpl<TargetSpecific::Default::RandImpl, ToType, Name>>
|
class FunctionRandom : public FunctionRandomImpl<TargetSpecific::Default::RandImpl, ToType, Name>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FunctionRandom(const Context & context_)
|
FunctionRandom(const Context & context) : selector(context)
|
||||||
: FunctionPerformanceAdaptor<FunctionRandomImpl<TargetSpecific::Default::RandImpl, ToType, Name>>(context_)
|
|
||||||
{
|
{
|
||||||
|
selector.registerImplementation<TargetArch::Default,
|
||||||
|
FunctionRandomImpl<TargetSpecific::Default::RandImpl, ToType, Name>>();
|
||||||
|
selector.registerImplementation<TargetArch::Default,
|
||||||
|
FunctionRandomImpl<TargetSpecific::Default::RandImpl2, ToType, Name>>();
|
||||||
|
|
||||||
if constexpr (UseMultitargetCode)
|
if constexpr (UseMultitargetCode)
|
||||||
{
|
{
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::SSE42::RandImpl, ToType, Name>>(TargetArch::SSE42);
|
selector.registerImplementation<TargetArch::SSE42,
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::AVX::RandImpl, ToType, Name>>(TargetArch::AVX);
|
FunctionRandomImpl<TargetSpecific::SSE42::RandImpl, ToType, Name>>();
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::AVX2::RandImpl, ToType, Name>>(TargetArch::AVX2);
|
selector.registerImplementation<TargetArch::AVX,
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::AVX512F::RandImpl, ToType, Name>>(TargetArch::AVX512F);
|
FunctionRandomImpl<TargetSpecific::AVX::RandImpl, ToType, Name>>();
|
||||||
|
selector.registerImplementation<TargetArch::AVX2,
|
||||||
|
FunctionRandomImpl<TargetSpecific::AVX2::RandImpl, ToType, Name>>();
|
||||||
|
selector.registerImplementation<TargetArch::AVX512F,
|
||||||
|
FunctionRandomImpl<TargetSpecific::AVX512F::RandImpl, ToType, Name>>();
|
||||||
|
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::Default::RandImpl2, ToType, Name>>(TargetArch::Default);
|
selector.registerImplementation<TargetArch::AVX2,
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::AVX2::RandImpl2, ToType, Name>>(TargetArch::AVX2);
|
FunctionRandomImpl<TargetSpecific::AVX2::RandImpl2, ToType, Name>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
||||||
|
{
|
||||||
|
selector.selectAndExecute(block, arguments, result, input_rows_count);
|
||||||
|
}
|
||||||
|
|
||||||
static FunctionPtr create(const Context & context)
|
static FunctionPtr create(const Context & context)
|
||||||
{
|
{
|
||||||
return std::make_shared<FunctionRandom<ToType, Name>>(context);
|
return std::make_shared<FunctionRandom<ToType, Name>>(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImplementationSelector<IFunction> selector;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -117,123 +117,67 @@ struct PerformanceStatistics
|
|||||||
PerformanceStatistics(ssize_t choose_method_) : choose_method(choose_method_) {}
|
PerformanceStatistics(ssize_t choose_method_) : choose_method(choose_method_) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PerformanceAdaptorOptions
|
/* Class which is used to store implementations for the function and selecting the best one to run
|
||||||
{
|
* based on processor architecture and statistics from previous runs.
|
||||||
std::optional<std::vector<String>> implementations;
|
*
|
||||||
};
|
* FunctionInterface is typically IFunction or IExecutableFunctionImpl, but practically it can be
|
||||||
|
* any interface that contains "execute" method (IFunction is an exception and is supported as well).
|
||||||
/// Redirects IExecutableFunctionImpl::execute() and IFunction:executeImpl() to executeFunctionImpl();
|
*
|
||||||
template <typename DefaultFunction, typename Dummy = void>
|
* Example of usage:
|
||||||
class FunctionExecutor;
|
*
|
||||||
|
* class MyDefaulImpl : public IFunction {...};
|
||||||
template <typename DefaultFunction>
|
* class MySecondImpl : public IFunction {...};
|
||||||
class FunctionExecutor<DefaultFunction, std::enable_if_t<std::is_base_of_v<IExecutableFunctionImpl, DefaultFunction>>>
|
* class MyAVX2Impl : public IFunction {...};
|
||||||
: public DefaultFunction
|
*
|
||||||
|
* /// All methods but execute/executeImpl are usually not bottleneck, so just use them from
|
||||||
|
* /// default implementation.
|
||||||
|
* class MyFunction : public MyDefaultImpl
|
||||||
|
* {
|
||||||
|
* MyFunction(const Context & context) : selector(context) {
|
||||||
|
* /// Register all implementations in constructor.
|
||||||
|
* /// There could be as many implementation for every target as you want.
|
||||||
|
* selector.registerImplementation<TargetArch::Default, MyDefaultImpl>();
|
||||||
|
* selector.registerImplementation<TargetArch::Default, MySecondImpl>();
|
||||||
|
* selector.registreImplementation<TargetArch::AVX2, MyAVX2Impl>();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void executeImpl(...) override {
|
||||||
|
* selector.selectAndExecute(...);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static FunctionPtr create(const Context & context) {
|
||||||
|
* return std::make_shared<MyFunction>(context);
|
||||||
|
* }
|
||||||
|
* private:
|
||||||
|
* ImplementationSelector<IFunction> selector;
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
template <typename FunctionInterface>
|
||||||
|
class ImplementationSelector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using BaseFunctionPtr = ExecutableFunctionImplPtr;
|
using ImplementationPtr = std::shared_ptr<FunctionInterface>;
|
||||||
|
|
||||||
template <typename ...Args>
|
ImplementationSelector(const Context & context_) : context(context_) {}
|
||||||
FunctionExecutor(Args&&... args) : DefaultFunction(std::forward<Args>(args)...) {}
|
|
||||||
|
|
||||||
virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0;
|
/* Select the best implementation based on previous runs.
|
||||||
|
* If FunctionInterface is IFunction, then "executeImpl" method of the implementation will be called
|
||||||
virtual void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
* and "execute" otherwise.
|
||||||
|
*/
|
||||||
|
void selectAndExecute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count)
|
||||||
{
|
{
|
||||||
executeFunctionImpl(block, arguments, result, input_rows_count);
|
if (implementations.empty())
|
||||||
}
|
throw Exception("There are no available implementations for function " "TODO(dakovalkov): add name",
|
||||||
};
|
|
||||||
|
|
||||||
template <typename DefaultFunction>
|
|
||||||
class FunctionExecutor<DefaultFunction, std::enable_if_t<std::is_base_of_v<IFunction, DefaultFunction>>>
|
|
||||||
: public DefaultFunction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using BaseFunctionPtr = FunctionPtr;
|
|
||||||
|
|
||||||
template <typename ...Args>
|
|
||||||
FunctionExecutor(Args&&... args) : DefaultFunction(std::forward<Args>(args)...) {}
|
|
||||||
|
|
||||||
virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0;
|
|
||||||
|
|
||||||
virtual void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
|
||||||
{
|
|
||||||
executeFunctionImpl(block, arguments, result, input_rows_count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Combine several IExecutableFunctionImpl into one.
|
|
||||||
/// All the implementations should be equivalent.
|
|
||||||
/// Implementation to execute will be selected based on performance on previous runs.
|
|
||||||
/// DefaultFunction should be executable on every supported platform, while alternative implementations
|
|
||||||
/// could use extended set of instructions (AVX, NEON, etc).
|
|
||||||
/// It's convenient to inherit your func from this and register all alternative implementations in the constructor.
|
|
||||||
template <typename DefaultFunction>
|
|
||||||
class FunctionPerformanceAdaptor : public FunctionExecutor<DefaultFunction>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using BaseFunctionPtr = typename FunctionExecutor<DefaultFunction>::BaseFunctionPtr;
|
|
||||||
|
|
||||||
template <typename ...Params>
|
|
||||||
FunctionPerformanceAdaptor(const Context & context_, Params&&... params)
|
|
||||||
: FunctionExecutor<DefaultFunction>(std::forward<Params>(params)...)
|
|
||||||
, context(context_)
|
|
||||||
{
|
|
||||||
if (isImplementationEnabled(DefaultFunction::getImplementationTag()))
|
|
||||||
statistics.emplace_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register alternative implementation.
|
|
||||||
template<typename Function, typename ...Params>
|
|
||||||
void registerImplementation(TargetArch arch, Params&&... params)
|
|
||||||
{
|
|
||||||
if (IsArchSupported(arch) && isImplementationEnabled(Function::getImplementationTag()))
|
|
||||||
{
|
|
||||||
impls.emplace_back(std::make_shared<Function>(std::forward<Params>(params)...));
|
|
||||||
statistics.emplace_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isImplementationEnabled(const String & impl_tag)
|
|
||||||
{
|
|
||||||
const String & tag = context.getSettingsRef().function_implementation.value;
|
|
||||||
return tag.empty() || tag == impl_tag;
|
|
||||||
// if (!options.implementations)
|
|
||||||
// return true;
|
|
||||||
|
|
||||||
// for (const auto & tag : *options.implementations)
|
|
||||||
// {
|
|
||||||
// if (tag == impl_tag)
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments,
|
|
||||||
size_t result, size_t input_rows_count) override
|
|
||||||
{
|
|
||||||
if (statistics.empty())
|
|
||||||
throw Exception("All available implementations are disabled by user config",
|
|
||||||
ErrorCodes::NO_SUITABLE_FUNCTION_IMPLEMENTATION);
|
ErrorCodes::NO_SUITABLE_FUNCTION_IMPLEMENTATION);
|
||||||
|
|
||||||
auto id = statistics.select();
|
auto id = statistics.select();
|
||||||
Stopwatch watch;
|
Stopwatch watch;
|
||||||
|
|
||||||
if (id == impls.size())
|
if constexpr (std::is_same_v<FunctionInterface, IFunction>)
|
||||||
{
|
implementations[id]->executeImpl(block, arguments, result, input_rows_count);
|
||||||
if constexpr (std::is_base_of_v<IFunction, FunctionPerformanceAdaptor>)
|
|
||||||
DefaultFunction::executeImpl(block, arguments, result, input_rows_count);
|
|
||||||
else
|
else
|
||||||
DefaultFunction::execute(block, arguments, result, input_rows_count);
|
implementations[id]->execute(block, arguments, result, input_rows_count);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if constexpr (std::is_base_of_v<IFunction, FunctionPerformanceAdaptor>)
|
|
||||||
impls[id]->executeImpl(block, arguments, result, input_rows_count);
|
|
||||||
else
|
|
||||||
impls[id]->execute(block, arguments, result, input_rows_count);
|
|
||||||
}
|
|
||||||
watch.stop();
|
watch.stop();
|
||||||
|
|
||||||
// TODO(dakovalkov): Calculate something more informative.
|
// TODO(dakovalkov): Calculate something more informative.
|
||||||
@ -249,10 +193,29 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Register new implementation for function.
|
||||||
|
*
|
||||||
|
* Arch - required instruction set for running the implementation. It's guarantied that no one method would
|
||||||
|
* be called (even the constructor and static methods) if the processor doesn't support this instruction set.
|
||||||
|
*
|
||||||
|
* FunctionImpl - implementation, should be inherited from template argument FunctionInterface.
|
||||||
|
*
|
||||||
|
* All function arguments will be forwarded to the implementation constructor.
|
||||||
|
*/
|
||||||
|
template <TargetArch Arch, typename FunctionImpl, typename ...Args>
|
||||||
|
void registerImplementation(Args&&... args)
|
||||||
|
{
|
||||||
|
if (IsArchSupported(Arch))
|
||||||
|
{
|
||||||
|
implementations.emplace_back(std::make_shared<FunctionImpl>(std::forward<Args>(args)...));
|
||||||
|
statistics.emplace_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<BaseFunctionPtr> impls; // Alternative implementations.
|
|
||||||
PerformanceStatistics statistics;
|
|
||||||
const Context & context;
|
const Context & context;
|
||||||
|
std::vector<ImplementationPtr> implementations;
|
||||||
|
PerformanceStatistics statistics;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,24 +29,35 @@ struct RandXorshiftImpl2
|
|||||||
) // DECLARE_MULTITARGET_CODE
|
) // DECLARE_MULTITARGET_CODE
|
||||||
|
|
||||||
template <typename ToType, typename Name>
|
template <typename ToType, typename Name>
|
||||||
class FunctionRandomXorshift
|
class FunctionRandomXorshift : public FunctionRandomImpl<TargetSpecific::Default::RandXorshiftImpl, ToType, Name>
|
||||||
: public FunctionPerformanceAdaptor<FunctionRandomImpl<TargetSpecific::Default::RandXorshiftImpl, ToType, Name>>
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FunctionRandomXorshift(const Context & context_)
|
FunctionRandomXorshift(const Context & context) : selector(context)
|
||||||
: FunctionPerformanceAdaptor<FunctionRandomImpl<TargetSpecific::Default::RandXorshiftImpl, ToType, Name>>(context_)
|
|
||||||
{
|
{
|
||||||
|
selector.registerImplementation<TargetArch::Default,
|
||||||
|
FunctionRandomImpl<TargetSpecific::Default::RandXorshiftImpl, ToType, Name>>();
|
||||||
|
|
||||||
if constexpr (UseMultitargetCode)
|
if constexpr (UseMultitargetCode)
|
||||||
{
|
{
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::AVX2::RandXorshiftImpl, ToType, Name>>(TargetArch::AVX2);
|
selector.registerImplementation<TargetArch::AVX2,
|
||||||
this->template registerImplementation<FunctionRandomImpl<TargetSpecific::AVX2::RandXorshiftImpl2, ToType, Name>>(TargetArch::AVX2);
|
FunctionRandomImpl<TargetSpecific::AVX2::RandXorshiftImpl, ToType, Name>>();
|
||||||
|
selector.registerImplementation<TargetArch::AVX2,
|
||||||
|
FunctionRandomImpl<TargetSpecific::AVX2::RandXorshiftImpl2, ToType, Name>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
||||||
|
{
|
||||||
|
selector.selectAndExecute(block, arguments, result, input_rows_count);
|
||||||
|
}
|
||||||
|
|
||||||
static FunctionPtr create(const Context & context)
|
static FunctionPtr create(const Context & context)
|
||||||
{
|
{
|
||||||
return std::make_shared<FunctionRandomXorshift<ToType, Name>>(context);
|
return std::make_shared<FunctionRandomXorshift<ToType, Name>>(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImplementationSelector<IFunction> selector;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user