thesis version

This commit is contained in:
2021-05-10 18:14:24 +02:00
commit caf2a692f9
281 changed files with 73182 additions and 0 deletions

10
inc/alsk/alsk.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef ALSK_ALSK_H
#define ALSK_ALSK_H
#include "context/context.h"
#include "edsl/edsl.h"
#include "executor/executor.h"
#include "impl/impl.h"
#include "skeleton/skeleton.h"
#endif

View File

@ -0,0 +1,90 @@
#ifndef ALSK_ALSK_CONTEXT_CONTEXT_H
#define ALSK_ALSK_CONTEXT_CONTEXT_H
#include <functional>
#include <tuple>
#include <vector>
#include <random>
#include "../skeleton/link/args/placeholders.h"
namespace alsk {
template<typename RNG>
class Context {
public:
using Id = std::size_t;
using Args = std::tuple<Id, RNG&>;
using Seed = decltype(+RNG::default_seed);
private:
Id _idCount;
std::vector<RNG> _rng;
std::vector<Args> _args;
public:
Seed seed;
public:
constexpr Context(): _idCount{0}, seed{RNG::default_seed} {}
void setup(Id idCount = 30) {
if(idCount <= _idCount) return;
Id baseId = _idCount;
_idCount = idCount;
RNG seeder{seed};
_rng.reserve(idCount);
for(Id id = baseId; id < idCount; ++id)
_rng.emplace_back(seeder());
_args.reserve(idCount);
for(Id id = baseId; id < idCount; ++id)
_args.emplace_back(id, std::ref(_rng[id]));
}
void reset() {
RNG seeder{seed};
_rng.clear();
for(Id id = 0; id < _idCount; ++id)
_rng.emplace_back(seeder());
_args.clear();
for(Id id = 0; id < _idCount; ++id)
_args.emplace_back(id, std::ref(_rng[id]));
}
constexpr Id maxId() const noexcept { return _idCount; }
Args& args(Id const& id) { return _args[id]; }
~Context() noexcept {}
};
using DefaultContext = Context<std::mt19937>;
namespace arg {
/**
* CtxId
* to get own's context identifier
*/
using CtxId = C<0>;
/**
* RNG
* for random number generator
*/
using RNG = C<1>;
}
}
#endif

19
inc/alsk/edsl/edsl.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef ALSK_ALSK_EDSL_EDSL_H
#define ALSK_ALSK_EDSL_EDSL_H
#include "implement.h"
#include "link.h"
#include "op/op.h"
namespace alsk {
namespace edsl {
template<typename Expression, std::enable_if_t<isOperand<std::decay_t<Expression>>>* = nullptr>
constexpr decltype(auto) seq(Expression&& expression) {
return &std::forward<Expression>(expression);
}
}
}
#endif

50
inc/alsk/edsl/implement.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef ALSK_ALSK_EDSL_IMPL_H
#define ALSK_ALSK_EDSL_IMPL_H
#include <utility>
#include "../executor/traits.h"
#include "../skeleton/utility.h"
#include "../impl/impl.h"
#include "op/impl/serial.h"
#include "op/traits.h"
#include "link.h"
namespace alsk {
namespace edsl {
template<typename Expression>
constexpr auto getSkeleton(Expression) -> BuildSkeletonT<typename Expression::Struct, typename Expression::Links>;
template<
template<typename> class Executor, typename Context = DefaultContext,
typename Expression,
std::enable_if_t<isBranch<std::decay_t<Expression>>>* = nullptr
>
constexpr auto implement(Expression&& expression) {
using Skeleton = decltype(getSkeleton(expression));
auto f = alsk::implement<Executor, Skeleton, Context>();
std::forward<Expression>(expression).setup(f.skeleton);
return f;
}
template<
template<typename> class Executor, typename Context = DefaultContext,
typename Expression,
std::enable_if_t<isLeaf<std::decay_t<Expression>>>* = nullptr
>
constexpr auto implement(Expression&& expression) {
using Signature = typename std::decay_t<Expression>::Signature;
using FixedSignatures = AutoLinkSerial<Signature>;
auto linkedExpression = link<typename FixedSignatures::in>(std::forward<Expression>(expression));
using LinkedExpression = std::decay_t<decltype(linkedExpression)>;
return implement<Executor, Context>(Serial<typename FixedSignatures::out, LinkedExpression>{std::move(linkedExpression)});
}
}
}
#endif

34
inc/alsk/edsl/link.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef ALSK_ALSK_EDSL_LINK_H
#define ALSK_ALSK_EDSL_LINK_H
#include <utility>
#include "op/op.h"
namespace alsk {
namespace edsl {
template<typename Signature, typename Expression>
constexpr decltype(auto) link(Expression&& expression) {
return std::forward<Expression>(expression).template link<Signature>();
}
template<typename Signature, typename... Expressions, std::enable_if_t<(sizeof...(Expressions) > 1)>* = nullptr>
constexpr decltype(auto) link(Expressions&&... expressions) {
return link<Signature>(serial(std::forward<Expressions>(expressions)...));
}
template<typename Signature, typename Type, std::enable_if_t<not isOperand<Type>>* = nullptr>
constexpr decltype(auto) link() {
return link<Signature>(makeOperand<Type>());
}
template<typename Signature, typename Type, std::enable_if_t<isOperand<Type>>* = nullptr>
constexpr decltype(auto) link() {
return link<Signature, Type>();
}
}
}
#endif

View File

@ -0,0 +1,85 @@
#ifndef ALSK_ALSK_EDSL_OP_IMPL_FARM_H
#define ALSK_ALSK_EDSL_OP_IMPL_FARM_H
#include <utility>
#include "../traits.h"
#include "../../../skeleton/bone/farm.h"
#include "../../../skeleton/struct/struct.h"
#include "../../../skeleton/link/link.h"
namespace alsk {
namespace edsl {
template<typename, typename, typename> struct FarmSel;
template<typename Signature_, typename Task>
struct Farm: OperandBase {
Task task;
unsigned int n;
constexpr Farm(Task task, unsigned int n)
noexcept(noexcept(Task{std::move(task)})):
task{std::move(task)}, n{n} {}
template<typename S>
constexpr Farm(Farm<S, Task> const& o)
noexcept(noexcept(Task{o.task})):
task{o.task}, n{o.n} {}
template<typename S>
constexpr Farm(Farm<S, Task>&& o)
noexcept(noexcept(Task{std::move(o.task)})):
task{std::move(o.task)}, n{std::move(o.n)} {}
using Signature = Signature_;
using Struct = S<alsk::Farm, typename Task::Struct>;
using Links = L<alsk::Farm, Signature, typename Task::Links>;
template<typename S>
constexpr void setup(S& skeleton) const {
skeleton.n = n;
setupFor(task, skeleton.task);
}
template<typename Signature>
constexpr auto link() const&& {
return Farm<Signature, Task>{std::move(*this)};
}
template<typename Signature>
constexpr auto link() const& {
return Farm<Signature, Task>{*this};
}
template<typename Rhs, std::enable_if_t<isOperand<Rhs>>* = nullptr>
constexpr auto select(Rhs const& rhs) {
return FarmSel<GetReturnType<Rhs>(), Task, Rhs>{task, rhs, n};
}
};
template<typename Signature = void(), typename Task, std::enable_if_t<isOperand<Task>>* = nullptr>
constexpr auto farm(Task const& task, unsigned int n = 0) {
return Farm<Signature, Task>{task, n};
}
template<typename Rhs, std::enable_if_t<isOperand<Rhs>>* = nullptr>
constexpr auto operator*(Rhs const& rhs) {
return Farm<void(), Rhs>{rhs, 0};
}
template<typename Rhs, std::enable_if_t<isOperand<Rhs>>* = nullptr>
constexpr auto operator*(unsigned int n, Rhs const& rhs) {
return Farm<void(), Rhs>{rhs, n};
}
template<typename Lhs, std::enable_if_t<isOperand<Lhs>>* = nullptr>
constexpr auto operator*(Lhs const& lhs, unsigned int n) {
return Farm<void(), Lhs>{lhs, n};
}
}
}
#endif

View File

@ -0,0 +1,70 @@
#ifndef ALSK_ALSK_EDSL_OP_IMPL_FARMSEL_H
#define ALSK_ALSK_EDSL_OP_IMPL_FARMSEL_H
#include <utility>
#include "../traits.h"
#include "../../../skeleton/bone/farmsel.h"
#include "../../../skeleton/struct/struct.h"
#include "../../../skeleton/link/link.h"
namespace alsk {
namespace edsl {
template<typename, typename> struct Farm;
template<typename Signature_, typename Task, typename Select>
struct FarmSel: OperandBase {
Task task;
Select select;
unsigned int n;
constexpr FarmSel(Task task, Select select, unsigned int n)
noexcept(noexcept(Task{std::move(task)}) and noexcept(Select{std::move(select)})):
task{std::move(task)}, select{std::move(select)}, n{n}
{}
template<typename S>
constexpr FarmSel(FarmSel<S, Task, Select> const& o)
noexcept(noexcept(Task{o.task}) and noexcept(Select{o.select})):
task{o.task}, select{o.select}, n{o.n}
{}
template<typename S>
constexpr FarmSel(FarmSel<S, Task, Select>&& o)
noexcept(noexcept(Task{std::move(o.task)}) and noexcept(Select{std::move(o.select)})):
task{std::move(o.task)}, select{std::move(o.select)}, n{std::move(o.n)}
{}
using Signature = Signature_;
using Struct = S<alsk::FarmSel, typename Task::Struct, typename Select::Struct>;
using Links = L<alsk::FarmSel, Signature, typename Task::Links, typename Select::Links>;
template<typename S>
constexpr void setup(S& skeleton) const {
skeleton.n = n;
setupFor(task, skeleton.task);
setupFor(select, skeleton.select);
}
template<typename Signature>
constexpr auto link() const&& {
return FarmSel<Signature, Task, Select>{std::move(*this)};
}
template<typename Signature>
constexpr auto link() const& {
return FarmSel<Signature, Task, Select>{*this};
}
};
template<typename FR, typename... FArgs, typename Task, typename Rhs, std::enable_if_t<isOperand<Rhs>>* = nullptr>
constexpr auto operator->*(Farm<FR(FArgs...), Task> const& farm, Rhs const& rhs) {
return FarmSel<GetReturnType<Rhs>(FArgs...), Task, Rhs>{farm.task, rhs, farm.n};
}
}
}
#endif

View File

@ -0,0 +1,99 @@
#ifndef ALSK_ALSK_EDSL_OP_IMPL_ITERSEL_H
#define ALSK_ALSK_EDSL_OP_IMPL_ITERSEL_H
#include <utility>
#include "../traits.h"
#include "../../../skeleton/bone/itersel.h"
#include "../../../skeleton/struct/struct.h"
#include "../../../skeleton/link/link.h"
namespace alsk {
namespace edsl {
template<typename, typename> struct Loop;
template<typename, typename, typename> struct FarmSel;
template<typename Signature_, typename Task, typename Select>
struct IterSel: OperandBase {
Task task;
Select select;
unsigned int n;
constexpr IterSel(Task task, Select select, unsigned int n)
noexcept(noexcept(Task{std::move(task)}) and noexcept(Select{std::move(select)})):
task{std::move(task)}, select{std::move(select)}, n{n}
{}
template<typename S>
constexpr IterSel(IterSel<S, Task, Select> const& o)
noexcept(noexcept(Task{o.task}) and noexcept(Select{o.select})):
task{o.task}, select{o.select}, n{o.n}
{}
template<typename S>
constexpr IterSel(IterSel<S, Task, Select>&& o)
noexcept(noexcept(Task{std::move(o.task)}) and noexcept(Select{std::move(o.select)})):
task{std::move(o.task)}, select{std::move(o.select)}, n{std::move(o.n)}
{}
using Signature = Signature_;
using Struct = S<alsk::IterSel, typename Task::Struct, typename Select::Struct>;
using Links = L<alsk::IterSel, Signature, typename Task::Links, typename Select::Links>;
template<typename S>
constexpr void setup(S& skeleton) const {
skeleton.n = n;
setupFor(task, skeleton.task);
setupFor(select, skeleton.select);
}
template<typename Signature>
constexpr auto link() const&& {
return IterSel<Signature, Task, Select>{std::move(*this)};
}
template<typename Signature>
constexpr auto link() const& {
return IterSel<Signature, Task, Select>{*this};
}
};
namespace impl {
template<typename, typename> struct Iter;
template<typename Task, typename R, typename... Args>
struct Iter<Task, R(Args...)> {
Task const& task;
unsigned int n;
template<typename Select, std::enable_if_t<isOperand<Select>>* = nullptr>
constexpr auto select(Select const& select) {
using Signature = GetReturnType<Select>(Args...);
return alsk::edsl::IterSel<Signature, Task, Select>{task, select, n};
}
};
}
template<typename Signature = void(), typename Task, std::enable_if_t<isOperand<Task>>* = nullptr>
constexpr auto iter(Task const& task, unsigned int n = 0) {
return impl::Iter<Task, Signature>{task, n};
}
template<typename Signature, typename Task, typename Select>
constexpr auto operator&(FarmSel<Signature, Task, Select> const& farmsel) {
return IterSel<Signature, Task, Select>{farmsel.task, farmsel.select, farmsel.n};
}
template<typename LR, typename... LArgs, typename Task, typename Rhs, std::enable_if_t<isOperand<Rhs>>* = nullptr>
constexpr auto operator->*(Loop<LR(LArgs...), Task> const& loop, Rhs const& rhs) {
return IterSel<GetReturnType<Rhs>(LArgs...), Task, Rhs>{loop.task, rhs, loop.n};
}
}
}
#endif

View File

@ -0,0 +1,73 @@
#ifndef ALSK_ALSK_EDSL_OP_IMPL_LOOP_H
#define ALSK_ALSK_EDSL_OP_IMPL_LOOP_H
#include <utility>
#include "../traits.h"
#include "../../../skeleton/bone/loop.h"
#include "../../../skeleton/struct/struct.h"
#include "../../../skeleton/link/link.h"
namespace alsk {
namespace edsl {
template<typename, typename> struct Farm;
template<typename Signature_, typename Task>
struct Loop: OperandBase {
Task task;
unsigned int n;
constexpr Loop(Task task, unsigned int n)
noexcept(noexcept(Task{std::move(task)})):
task{std::move(task)}, n{n}
{}
template<typename S>
constexpr Loop(Loop<S, Task> const& o)
noexcept(noexcept(Task{o.task})):
task{o.task}, n{o.n}
{}
template<typename S>
constexpr Loop(Loop<S, Task>&& o)
noexcept(noexcept(Task{std::move(o.task)})):
task{std::move(o.task)}, n{std::move(o.n)}
{}
using Signature = Signature_;
using Struct = S<alsk::Loop, typename Task::Struct>;
using Links = L<alsk::Loop, Signature, typename Task::Links>;
template<typename S>
constexpr void setup(S& skeleton) const {
skeleton.n = n;
setupFor(task, skeleton.task);
}
template<typename Signature>
constexpr auto link() const&& {
return Loop<Signature, Task>{std::move(*this)};
}
template<typename Signature>
constexpr auto link() const& {
return Loop<Signature, Task>{*this};
}
};
template<typename Signature = void(), typename Task>
constexpr auto loop(Task const& task, unsigned int n = 0) {
return Loop<Signature, Task>{task, n};
}
template<typename Signature, typename Task>
constexpr auto operator&(Farm<Signature, Task> const& farm) {
return Loop<Signature, Task>{farm.task, farm.n};
}
}
}
#endif

View File

@ -0,0 +1,67 @@
#ifndef ALSK_ALSK_EDSL_OP_IMPL_OPERAND_H
#define ALSK_ALSK_EDSL_OP_IMPL_OPERAND_H
#include <type_traits>
#include <utility>
#include "../../../skeleton/muscle/muscle.h"
namespace alsk {
namespace edsl {
struct OperandTag;
struct LeafTag;
struct OperandBase {
using IsOperand = OperandTag;
constexpr OperandBase() noexcept = default;
template<typename S, typename Operand, decltype(std::declval<Operand>().setup(std::declval<S&>()))* = nullptr>
constexpr void setupFor(Operand& operand, S& skeleton) const noexcept {
operand.setup(skeleton);
}
template<typename... Args>
constexpr void setupFor(Args&&...) const noexcept {}
};
template<typename Function_, typename Signature_>
struct Operand: OperandBase {
using IsLeaf = LeafTag;
using Function = Function_;
using Signature = Signature_;
using Struct = Function_;
using Links = Signature_;
constexpr Operand() noexcept = default;
template<typename Signature>
constexpr auto link() const noexcept { return Operand<Function, Signature>{}; }
};
template<typename Function>
constexpr auto makeOperand() noexcept {
return Operand<Function, void()>{};
}
template<typename Signature, typename Function>
constexpr auto makeOperand() noexcept {
return Operand<Function, Signature>{};
}
template<typename Signature, Signature function>
constexpr auto makeOperand() noexcept {
using Function = Fn<Signature, function>;
return Operand<Function, typename Function::Signature>{};
}
}
}
// TODO C++17
#define alskMakeOperand(f) makeOperand<decltype(f)&, f>()
#endif

View File

@ -0,0 +1,120 @@
#ifndef ALSK_ALSK_EDSL_OP_IMPL_SERIAL_H
#define ALSK_ALSK_EDSL_OP_IMPL_SERIAL_H
#include <utility>
#include "../traits.h"
#include "../../../skeleton/bone/serial.h"
#include "../../../skeleton/struct/struct.h"
#include "../../../skeleton/link/link.h"
namespace alsk {
namespace edsl {
template<typename Signature_, typename... Operands>
struct Serial: OperandBase {
std::tuple<Operands...> operands;
constexpr Serial(Operands... operands): operands{operands...} {}
template<typename S>
constexpr Serial(Serial<S, Operands...> const& o): operands{o.operands} {}
template<typename S>
constexpr Serial(Serial<S, Operands...>&& o): operands{std::move(o.operands)} {}
using Signature = Signature_;
using Struct = S<alsk::Serial, typename Operands::Struct...>;
using Links = L<alsk::Serial, Signature, typename Operands::Links...>;
template<typename S>
constexpr void setup(S& skeleton) const {
setup(skeleton, std::make_index_sequence<sizeof...(Operands)>());
}
template<typename S, std::size_t... indices>
constexpr void setup(S& skeleton, std::index_sequence<indices...>) const {
using Expander = int[];
static_cast<void>(Expander{(OperandBase::setupFor(std::get<indices>(operands), skeleton.template task<indices>()), 0)...});
}
template<typename Signature>
constexpr auto link() const&& {
return Serial<Signature, Operands...>{std::move(*this)};
}
template<typename Signature>
constexpr auto link() const& {
return Serial<Signature, Operands...>{*this};
}
};
namespace impl {
template<typename Lhs, std::size_t... lIndices, typename Rhs, std::size_t... rIndices>
constexpr auto mergeSerial(Lhs const& lhs, std::index_sequence<lIndices...>, Rhs const& rhs, std::index_sequence<rIndices...>) {
return Serial<void(), std::tuple_element_t<lIndices, Lhs>..., std::tuple_element_t<rIndices, Rhs>...>{
std::get<lIndices>(lhs)..., std::get<rIndices>(rhs)...
};
}
template<typename Lhs, typename Rhs>
constexpr auto mergeSerial(Lhs const& lhs, Rhs const& rhs) {
return mergeSerial(lhs, std::make_index_sequence<std::tuple_size<Lhs>{}>(),
rhs, std::make_index_sequence<std::tuple_size<Rhs>{}>());
}
template<typename T>
constexpr auto operandTuple(T const& t) {
return std::make_tuple(t);
}
template<typename... Ts>
constexpr auto operandTuple(Serial<Ts...> const& s) noexcept {
return s.operands;
}
template<typename...> struct SerialBuilder;
template<typename Arg, typename... Args>
struct SerialBuilder<Arg, Args...> {
static constexpr auto build(Arg const& arg, Args const&... args) {
return mergeSerial(operandTuple(arg), operandTuple(SerialBuilder<Args...>::build(args...)));
}
};
template<typename Arg>
struct SerialBuilder<Arg> {
static constexpr auto build(Arg const& arg) {
return arg;
}
};
}
template<typename... Args, std::enable_if_t<(sizeof...(Args) > 1) and allOperands<Args...>>* = nullptr>
constexpr auto serial(Args const&... args) {
return impl::SerialBuilder<Args...>::build(args...);
}
template<
typename Lhs, typename Rhs,
std::enable_if_t<isOperand<Lhs> and isOperand<Rhs>>* = nullptr
>
constexpr auto operator,(Lhs const& lhs, Rhs const& rhs) {
return impl::mergeSerial(impl::operandTuple(lhs), impl::operandTuple(rhs));
}
template<
typename Lhs, typename Rhs,
std::enable_if_t<isOperand<Lhs> and isOperand<Rhs>>* = nullptr
>
constexpr auto operator&(Lhs const& lhs, Rhs const& rhs) {
return impl::mergeSerial(impl::operandTuple(lhs), impl::operandTuple(rhs));
}
}
}
#endif

View File

@ -0,0 +1,80 @@
#ifndef ALSK_ALSK_EDSL_OP_IMPL_WHILE_H
#define ALSK_ALSK_EDSL_OP_IMPL_WHILE_H
#include <utility>
#include "../traits.h"
#include "../../../skeleton/bone/while.h"
#include "../../../skeleton/struct/struct.h"
#include "../../../skeleton/link/link.h"
namespace alsk {
namespace edsl {
template<typename Signature_, typename Cond, typename Task>
struct While: OperandBase {
Cond cond;
Task task;
constexpr While(Cond cond, Task task)
noexcept(noexcept(Cond{std::move(cond)}) and noexcept(Task{std::move(task)})):
cond{std::move(cond)}, task{std::move(task)}
{}
template<typename S>
constexpr While(While<S, Cond, Task> const& o)
noexcept(noexcept(Cond{o.cond}) and noexcept(Task{o.task})):
cond{o.cond}, task{o.task}
{}
template<typename S>
constexpr While(While<S, Cond, Task>&& o)
noexcept(noexcept(Cond{std::move(o.cond)}) and noexcept(Task{std::move(o.task)})):
cond{std::move(o.cond)}, task{std::move(o.task)}
{}
using Signature = Signature_;
using Struct = S<alsk::While, typename Cond::Struct, typename Task::Struct>;
using Links = L<alsk::While, Signature, typename Cond::Links, typename Task::Links>;
template<typename S>
constexpr void setup(S& skeleton) const {
setupFor(cond, skeleton.cond);
setupFor(task, skeleton.task);
}
template<typename Signature>
constexpr auto link() const&& {
return While<Signature, Cond, Task>{std::move(*this)};
}
template<typename Signature>
constexpr auto link() const& {
return While<Signature, Cond, Task>{*this};
}
};
namespace impl {
template<typename Cond, typename Signature>
struct WhilePart {
Cond const& cond;
template<typename Task, std::enable_if_t<isOperand<Task>>* = nullptr>
constexpr auto do_(Task const& task) {
return alsk::edsl::While<Signature, Cond, Task>{cond, task};
}
};
}
template<typename Signature = void(), typename Cond, std::enable_if_t<isOperand<Cond>>* = nullptr>
constexpr auto while_(Cond const& cond) {
return impl::WhilePart<Cond, Signature>{cond};
}
}
}
#endif

12
inc/alsk/edsl/op/op.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef ALSK_ALSK_EDSL_OP_OP_H
#define ALSK_ALSK_EDSL_OP_OP_H
#include "impl/farm.h"
#include "impl/farmsel.h"
#include "impl/itersel.h"
#include "impl/loop.h"
#include "impl/operand.h"
#include "impl/serial.h"
#include "impl/while.h"
#endif

82
inc/alsk/edsl/op/traits.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef ALSK_ALSK_EDSL_OP_TRAITS_H
#define ALSK_ALSK_EDSL_OP_TRAITS_H
#include <type_traits>
#include "impl/operand.h"
namespace alsk {
namespace edsl {
/**
* @brief true if the type is an operand for the EDSL
*/
template<typename, typename=void> struct IsOperand: std::false_type {};
template<typename T>
struct IsOperand<T, std::enable_if_t<std::is_same<typename T::IsOperand, OperandTag>{}>>: std::true_type {};
template<typename T>
constexpr bool isOperand = IsOperand<T>::value;
/**
* @brief true if all the types are operands for the EDSL
*/
template<typename...> struct AllOperands;
template<typename T, typename... Ts>
struct AllOperands<T, Ts...> {
static constexpr bool value = isOperand<T> && AllOperands<Ts...>::value;
};
template<>
struct AllOperands<>: std::true_type {};
template<typename... Ts>
constexpr bool allOperands = AllOperands<Ts...>::value;
/**
* @brief true if the type is a leaf
*/
template<typename, typename=void> struct IsLeaf: std::false_type {};
template<typename T>
struct IsLeaf<T, std::enable_if_t<std::is_same<typename T::IsLeaf, LeafTag>{}>>: std::true_type {};
template<typename T>
constexpr bool isLeaf = IsLeaf<T>::value;
/**
* @brief true if the type is a branch
*/
template<typename, typename=void> struct IsBranch: std::false_type {};
template<typename T>
struct IsBranch<T, std::enable_if_t<isOperand<T> and not isLeaf<T>>>: std::true_type {};
template<typename T>
constexpr bool isBranch = IsBranch<T>::value;
/**
* @brief get the return value from an operand's signature
*/
template<typename> struct GetReturnTypeFromSignatureImpl;
template<typename R, typename... Args>
struct GetReturnTypeFromSignatureImpl<R(Args...)> {
using type = R;
};
template<typename Signature>
using GetReturnTypeFromSignature = typename GetReturnTypeFromSignatureImpl<Signature>::type;
/**
* @brief get the return value from an operand
*/
template<typename T>
using GetReturnType = GetReturnTypeFromSignature<typename T::Signature>;
}
}
#endif

View File

@ -0,0 +1,13 @@
#ifndef ALSK_ALSK_EXECUTOR_EXECUTOR_H
#define ALSK_ALSK_EXECUTOR_EXECUTOR_H
#include "impl/dynamicpool.h"
#include "impl/firstlevel/equi.h"
#include "impl/firstlevel/greedy.h"
#include "impl/firstlevel/noopti.h"
#include "impl/sequential.h"
#include "impl/staticpool.h"
#include "impl/staticpoolid.h"
#include "impl/staticthread.h"
#endif

View File

@ -0,0 +1,125 @@
#ifndef ALSK_ALSK_EXECUTOR_EXECUTORBASE_H
#define ALSK_ALSK_EXECUTOR_EXECUTORBASE_H
#include <algorithm>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
#include "tags.h"
#include "../impl/tags.h"
namespace alsk {
namespace exec {
struct ExecutorBase {
using IsExecutor = tag::Executor;
public:
struct Info {};
struct RCores {
std::vector<std::size_t> coresList;
RCores() { upTo(std::thread::hardware_concurrency()); }
/**
* @brief disables repeatability
*/
void disabled() noexcept { coresList.clear(); }
/**
* @brief defines possibles cores from min to n by given step
* @param n possibly included upper bound, if 0 or 1, disables repeatability
* @param min lower bound (at least 2, at most n)
* @param step step (at least 1)
*/
void upTo(std::size_t n, std::size_t min = 2, std::size_t step = 1) {
coresList.clear();
if(n < 2) return;
std::size_t k = (n-min+step) / step;
coresList.resize(k);
std::generate_n(std::begin(coresList), n-1, [i=0, &min, &step]() mutable { return (min+step*i++); });
}
/**
* @brief defines possibles cores from min to n, multiplying by given step
* @param n possibly included upper bound, if 0 or 1, disables repeatability
* @param min lower bound (at least 2, at most n)
* @param step step (at least 2)
*/
void expUpTo(std::size_t n, std::size_t min = 2, std::size_t step = 2) {
coresList.clear();
if(n < 2) return;
while(min <= n) {
coresList.push_back(min);
min *= step;
}
}
/**
* @brief defines possibles cores from min to n, multiplying by given step
* @param args all cores to support
*/
template<typename... Args>
void forValues(Args&&... args) {
coresList = {std::forward<Args>(args)...};
}
};
public:
/**
* @brief set this variable to the number of allotted cores
*/
std::size_t cores;
/**
* @brief this variable allows to configure repeatability
*/
RCores repeatability;
public:
ExecutorBase(): cores{std::thread::hardware_concurrency()} {}
public:
template<typename Impl>
void config(Impl&) {}
template<typename Impl>
std::size_t contextIdCount(Impl&, std::size_t id) { return id; }
template<typename Impl>
std::size_t contextId(Impl&, std::size_t id) { return id; }
template<typename Task, typename Impl, typename BTask, typename Parameters, typename Results, typename... Args>
decltype(auto) execute(Impl& impl, BTask& task, Parameters&& parameters, Results&& results, Args&&... args) {
return _execute<Task>(impl, task, impl.executorInfo, std::forward<Parameters>(parameters), std::forward<Results>(results),
std::forward<Args>(args)...);
}
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeSequential(Impl& impl, BTask& task, Parameters&& parameters, std::size_t n) {
return _executeSequential<Task>(impl, task, impl.executorInfo, std::forward<Parameters>(parameters), n);
}
protected:
template<typename Task, typename Impl, typename BTask, typename Info, typename Parameters, typename Results, typename... Args>
decltype(auto) _execute(Impl& impl, BTask& task, Info&& info, Parameters&& parameters, Results&& results, Args&&... args) {
return Task::execute(
impl, task, 0, std::forward<Info>(info), std::forward<Parameters>(parameters), std::forward<Results>(results),
std::forward<Args>(args)...
);
}
template<typename Task, typename Impl, typename BTask, typename Info, typename Parameters>
void _executeSequential(Impl& impl, BTask& task, Info const& info, Parameters const& parameters, std::size_t n) {
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
};
}
}
#endif

View File

@ -0,0 +1,12 @@
#ifndef ALSK_ALSK_EXECUTOR_EXECUTORSTATE_H
#define ALSK_ALSK_EXECUTOR_EXECUTORSTATE_H
namespace alsk {
namespace exec {
template<typename> struct ExecutorState;
}
}
#endif

View File

@ -0,0 +1,123 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_DYNAMICPOOL_H
#define ALSK_ALSK_EXECUTOR_IMPL_DYNAMICPOOL_H
#include <algorithm>
#include <future>
#include <vector>
#include "../executorbase.h"
#include "../executorstate.h"
#include "../../skeleton/traits.h"
#include "../utility/pool.h"
namespace alsk {
namespace exec {
template<typename S>
struct DynamicPool: ExecutorBase {
using Tag = alsk::tag::Parallel;
public:
std::size_t maxTaskCount = 1'000;
public:
template<typename Impl>
void config(Impl& impl) {
impl.state.executor.config(cores);
}
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
std::size_t taskCount = std::min(maxTaskCount, n);
if(cores > 1 && taskCount > 1) {
Info info;
std::vector<std::future<void>> futures(taskCount);
std::size_t const step = n/taskCount;
std::size_t const remain = n - step*(taskCount-1);
typename Impl::State& state = impl.state;
auto run = [&](std::size_t b, std::size_t k) {
for(std::size_t i = 0; i < k; ++i)
Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
};
for(std::size_t i = 0; i < taskCount-1; ++i)
futures[i] = state.executor.run([&, b=i*step, k=step]{ run(b, k); });
futures[taskCount-1] = state.executor.run([&, b=(taskCount-1)*step, k=remain]{ run(b, k); });
state.executor.wait(futures);
} else {
Info info;
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
std::size_t taskCount = std::min(maxTaskCount, n);
Value best{};
if(cores > 1 && taskCount > 1) {
Info info;
std::vector<std::future<void>> futures(taskCount);
std::size_t const step = n/taskCount;
std::size_t const remainBase = n - step*taskCount;
std::size_t remain = remainBase;
typename Impl::State& state = impl.state;
auto run = [&](Value& out, std::size_t b, std::size_t k) {
Value best{};
if(k)
best = Task::execute(impl, task, b+0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < k; ++i) {
Value current = Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
out = std::move(best);
};
std::size_t start{};
std::vector<Value> bests(taskCount);
for(std::size_t i = 0; i < taskCount-1; ++i) {
std::size_t offset = !!remain;
remain -= offset;
futures[i] = state.executor.run([&, &best=bests[i], b=start, k=step+offset] { run(best, b, k); });
start += step+offset;
}
futures[taskCount-1] = state.executor.run([&, &best=bests[taskCount-1], b=start, k=step] { run(best, b, k); });
state.executor.wait(futures);
if(taskCount) best = std::move(bests[0]);
for(std::size_t i = 1; i < taskCount; ++i)
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(bests[i]), std::move(best));
} else {
Info info;
if(n)
best = Task::execute(impl, task, 0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < n; ++i) {
Value current = Task::execute(impl, task, i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
return best;
}
};
template<typename S>
struct ExecutorState<DynamicPool<S>>: util::Pool {};
}
}
#endif

View File

@ -0,0 +1,195 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_FIRSTLEVEL_EQUI_H
#define ALSK_ALSK_EXECUTOR_IMPL_FIRSTLEVEL_EQUI_H
#include <cmath>
#include <thread>
#include <set>
#include <vector>
#include "../../executorbase.h"
#include "../../executorstate.h"
#include "../../../skeleton/traits.h"
namespace alsk {
namespace exec {
template<typename S>
struct FirstLevelEqui: ExecutorBase {
using Tag = alsk::tag::Parallel;
public:
struct Info {
unsigned int parDepth;
Info(unsigned int parDepth = 0) noexcept: parDepth{parDepth} {}
Info par() const noexcept { return {parDepth+1}; }
Info seq() const noexcept { return {parDepth}; }
};
private:
template<typename Impl>
void buildSplit(Impl& impl) {
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
split.clear();
auto traverser = [](std::size_t, auto&& skl, auto&&... values) {
using Skl = decltype(skl);
using Traits = SkeletonTraitsT<std::decay_t<Skl>>;
if(Traits::serial) return max(decltype(values)(values)...);
return Traits::parallelizability(std::forward<Skl>(skl));
};
auto firstLevelPar = SkeletonTraversal<S>::execute(impl.skeleton, traverser, 1ul);
split.insert(0);
for(auto const& k: repeatability.coresList) {
std::size_t start{};
std::size_t const step = firstLevelPar/k;
std::size_t remain = firstLevelPar - step*k;
for(unsigned int i = 0; i < k-1; ++i) {
std::size_t offset = !!remain;
remain -= offset;
start += step+offset;
split.insert(start * (state.executor.parTasksCount/firstLevelPar));
}
}
}
unsigned int threadLimit(Info const& info) const noexcept {
return info.parDepth? 1 : cores;
}
public:
template<typename Impl>
void config(Impl& impl) {
typename Impl::State& state = impl.state;
state.executor.parTasksCount = impl.parallelTasksCount();;
buildSplit(impl);
}
template<typename Impl>
std::size_t contextIdCount(Impl& impl, std::size_t) {
typename Impl::State& state = impl.state;
return state.executor.split.size();
}
template<typename Impl>
std::size_t contextId(Impl& impl, std::size_t id) { // O(log(n))
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
return std::distance(std::begin(split), split.upper_bound(id)) - 1;
}
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
std::size_t const maxThreads = threadLimit(impl.executorInfo);
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 1) {
Info info = impl.executorInfo.par();
std::vector<std::thread> threads(nThreads-1);
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
auto run = [&](std::size_t b, std::size_t k) {
for(std::size_t i = 0; i < k; ++i)
Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
};
{
std::size_t start{};
for(std::size_t i = 0; i < nThreads-1; ++i) {
std::size_t offset = !!remain;
remain -= offset;
threads[i] = std::thread{run, start, step+offset};
start += step+offset;
}
run(start, step);
}
for(std::thread& thread: threads) thread.join();
} else {
Info info = impl.executorInfo.seq();
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
std::size_t const maxThreads = threadLimit(impl.executorInfo); // TODO? fix neighbours
Value best{};
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 1) {
Info info = impl.executorInfo.par();
std::vector<std::thread> threads(nThreads-1);
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
auto run = [&](Value& out, std::size_t b, std::size_t k) {
Value best{};
if(k)
best = Task::execute(impl, task, b+0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < k; ++i) {
Value current = Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
out = std::move(best);
};
std::size_t start{};
std::vector<Value> bests(nThreads);
{
std::size_t i;
for(i = 0; i < nThreads-1; ++i) {
std::size_t offset = !!remain;
remain -= offset;
threads[i] = std::thread{run, std::ref(bests[i]), start, step+offset};
start += step+offset;
}
run(bests[i], start, step);
}
for(std::thread& thread: threads) thread.join();
if(nThreads) best = std::move(bests[0]);
for(std::size_t i = 1; i < nThreads; ++i)
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(bests[i]), std::move(best));
} else {
Info info = impl.executorInfo.seq();
if(n)
best = Task::execute(impl, task, 0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < n; ++i) {
Value current = Task::execute(impl, task, i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
return best;
}
};
template<typename S>
struct ExecutorState<FirstLevelEqui<S>> {
std::size_t parTasksCount;
std::set<std::size_t> split;
};
}
}
#endif

View File

@ -0,0 +1,168 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_FIRSTLEVEL_GREEDY_H
#define ALSK_ALSK_EXECUTOR_IMPL_FIRSTLEVEL_GREEDY_H
#include <cmath>
#include <thread>
#include <set>
#include <vector>
#include "../../executorbase.h"
#include "../../executorstate.h"
#include "../../../skeleton/traits.h"
namespace alsk {
namespace exec {
template<typename S>
struct FirstLevelGreedy: ExecutorBase {
using Tag = alsk::tag::Parallel;
public:
struct Info {
unsigned int parDepth;
};
private:
template<typename Impl>
void buildSplit(Impl& impl) {
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
split.clear();
auto traverser = [](std::size_t, auto&& skl, auto&&... values) {
using Skl = decltype(skl);
using Traits = alsk::SkeletonTraitsT<Skl>;
if(Traits::serial) return max(values...);
return Traits::parallelizability(std::forward<Skl>(skl));
};
auto firstLevelPar = SkeletonTraversal<S>::execute(impl.skeleton, traverser, 1ul);
split.insert(0);
for(auto const& k: repeatability.coresList) {
std::size_t start{};
std::size_t const step = (firstLevelPar + k-1)/k;
std::size_t const rk = (firstLevelPar + step-1)/step;
for(unsigned int i = 0; i < rk; ++i, start += step)
split.insert(start * (state.executor.parTasksCount/firstLevelPar));
}
}
unsigned int threadLimit(unsigned int level) const { return level? 1 : cores; }
public:
template<typename Impl>
void config(Impl& impl) {
typename Impl::State& state = impl.state;
state.executor.parTasksCount = impl.parallelTasksCount();;
buildSplit(impl);
}
template<typename Impl>
std::size_t contextIdCount(Impl& impl, std::size_t) {
typename Impl::State& state = impl.state;
return state.executor.split.size();
}
template<typename Impl>
std::size_t contextId(Impl& impl, std::size_t id) { // O(log(n))
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
return std::distance(std::begin(split), split.upper_bound(id)) - 1;
}
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
auto const& parDepth = impl.executorInfo.parDepth;
std::size_t const maxThreads = threadLimit(parDepth);
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 1) {
Info info{parDepth+1};
std::vector<std::thread> threads(nThreads-1);
std::size_t const step = std::round(static_cast<double>(n)/nThreads);
auto run = [&](std::size_t b, std::size_t k) {
for(std::size_t i = 0; i < k; ++i)
Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
};
for(std::size_t i = 0; i < nThreads-1; ++i)
threads[i] = std::thread{run, i*step, step};
run((nThreads-1)*step, n-(nThreads-1)*step);
for(std::thread& thread: threads) thread.join();
} else {
Info info{parDepth};
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
auto const& parDepth = impl.executorInfo.parDepth;
std::size_t const maxThreads = threadLimit(parDepth); // TODO fix neighbours
Value best{};
std::size_t const nThreadsBase = std::min(n, maxThreads);
if(nThreadsBase > 1) {
Info info{parDepth+1};
std::size_t const step = (n+nThreadsBase-1)/nThreadsBase;
std::size_t const nThreads = (n+step-1)/step;
std::vector<std::thread> threads(nThreads-1);
auto run = [&](Value& out, std::size_t b, std::size_t k) {
Value best{};
if(k)
best = Task::execute(impl, task, b+0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < k; ++i) {
Value current = Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
out = std::move(best);
};
std::size_t start{};
std::vector<Value> bests(nThreads);
for(std::size_t i = 0; i < nThreads-1; ++i, start += step)
threads[i] = std::thread{run, std::ref(bests[i]), start, step};
run(bests[nThreads-1], start, n - step*(nThreads-1));
for(std::thread& thread: threads) thread.join();
if(nThreads) best = std::move(bests[0]);
for(std::size_t i = 1; i < nThreads; ++i)
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(bests[i]), std::move(best));
} else {
Info info{parDepth};
if(n)
best = Task::execute(impl, task, 0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < n; ++i) {
Value current = Task::execute(impl, task, i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
return best;
}
};
template<typename S>
struct ExecutorState<FirstLevelGreedy<S>> {
std::size_t parTasksCount;
std::set<std::size_t> split;
};
}
}
#endif

View File

@ -0,0 +1,129 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_FIRSTLEVEL_NOOPTI_H
#define ALSK_ALSK_EXECUTOR_IMPL_FIRSTLEVEL_NOOPTI_H
#include <thread>
#include <set>
#include <cmath>
#include <vector>
#include "../../executorbase.h"
#include "../../executorstate.h"
#include "../../../skeleton/traits.h"
namespace alsk {
namespace exec {
template<typename S>
struct FirstLevelNoOpti: ExecutorBase {
using Tag = alsk::tag::Parallel;
public:
struct Info {
unsigned int parDepth;
};
private:
unsigned int threadLimit(unsigned int level) const { return level? 1 : cores; }
public:
template<typename Impl>
std::size_t contextIdCount(Impl&, std::size_t count) { return count; }
template<typename Impl>
std::size_t contextId(Impl&, std::size_t id) { return id; }
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
auto const& parDepth = impl.executorInfo.parDepth;
std::size_t const maxThreads = threadLimit(parDepth);
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 1) {
Info info{parDepth+1};
std::vector<std::thread> threads(nThreads-1);
std::size_t const step = std::round(static_cast<double>(n)/nThreads);
auto run = [&](std::size_t b, std::size_t k) {
for(std::size_t i = 0; i < k; ++i)
Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
};
for(std::size_t i = 0; i < nThreads-1; ++i)
threads[i] = std::thread{run, i*step, step};
run((nThreads-1)*step, n-(nThreads-1)*step);
for(std::thread& thread: threads) thread.join();
} else {
Info info{parDepth};
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
auto const& parDepth = impl.executorInfo.parDepth;
std::size_t const maxThreads = threadLimit(parDepth); // TODO fix neighbours
Value best{};
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 1) {
Info info{parDepth+1};
std::vector<std::thread> threads(nThreads-1);
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
auto run = [&](Value& out, std::size_t b, std::size_t k) {
Value best{};
if(k)
best = Task::execute(impl, task, b+0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < k; ++i) {
Value current = Task::execute(impl, task, b+i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
out = std::move(best);
};
std::size_t start{};
std::vector<Value> bests(nThreads);
for(std::size_t i = 0; i < nThreads-1; ++i) {
std::size_t offset = !!remain;
remain -= offset;
threads[i] = std::thread{run, std::ref(bests[i]), start, step+offset};
start += step+offset;
}
run(bests[nThreads-1], start, step);
for(std::thread& thread: threads) thread.join();
if(nThreads) best = std::move(bests[0]);
for(std::size_t i = 1; i < nThreads; ++i)
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(bests[i]), std::move(best));
} else {
Info info{parDepth};
if(n)
best = Task::execute(impl, task, 0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < n; ++i) {
Value current = Task::execute(impl, task, i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
return best;
}
};
template<typename S>
struct ExecutorState<FirstLevelNoOpti<S>> {};
}
}
#endif

View File

@ -0,0 +1,55 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_SEQUENTIAL_H
#define ALSK_ALSK_EXECUTOR_IMPL_SEQUENTIAL_H
#include <set>
#include <cmath>
#include <vector>
#include "../executorbase.h"
#include "../executorstate.h"
#include "../../skeleton/traits.h"
namespace alsk {
namespace exec {
template<typename S>
struct Sequential: ExecutorBase {
using Tag = alsk::tag::Sequential;
public:
template<typename Impl>
std::size_t contextIdCount(Impl&, std::size_t) { return 1; }
template<typename Impl>
std::size_t contextId(Impl&, std::size_t) { return 0; }
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
Info info;
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
Info info;
Value best{};
if(n)
best = Task::execute(impl, task, 0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < n; ++i) {
Value current = Task::execute(impl, task, i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
return best;
}
};
template<typename S>
struct ExecutorState<Sequential<S>> {};
}
}
#endif

View File

@ -0,0 +1,272 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_STATICPOOL_H
#define ALSK_ALSK_EXECUTOR_IMPL_STATICPOOL_H
#include <algorithm>
#include <cmath>
#include <future>
#include <map>
#include <set>
#include <vector>
#include "../executorbase.h"
#include "../executorstate.h"
#include "../../skeleton/traits.h"
#include "../utility/staticpool.h"
namespace alsk {
namespace exec {
template<typename S>
struct StaticPool: ExecutorBase {
using Tag = alsk::tag::Parallel;
public:
struct Info {
std::size_t cores;
std::size_t offset;
Info(std::size_t cores = 0, std::size_t offset = 0):
cores{cores}, offset{offset} {}
};
private:
auto buildSplitFor(S& s, std::size_t cores) {
std::map<std::size_t, std::size_t> ms;
auto populateSplitImpl = [&ms, totalCores=cores](
auto& buildSplitImpl, auto& s,
std::size_t maxThreads, std::size_t thOffset,
std::size_t n, std::size_t idStep, std::size_t id, bool isRemainder
) {
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 0) {
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
std::size_t const coresA = maxThreads/nThreads;
std::size_t const coresB = remainBase? maxThreads/remainBase : 1;
std::size_t start = 0;
for(std::size_t i = 0; i < nThreads; ++i) {
std::size_t thNum = thOffset + i*coresA;
std::size_t offset = !!remain;
remain -= offset;
if(!ms.count(id+start*idStep))
ms[id+start*idStep] = thNum;
for(std::size_t j = 0; j < step; ++j)
buildSplitImpl(s, coresA, thNum, id+(start+j)*idStep, false);
if(offset)
buildSplitImpl(s, coresB, thNum, id+(start+step)*idStep, true);
start += step+offset;
}
if(isRemainder) ms[id+start*idStep] = totalCores;
} else {
for(std::size_t i = 0; i < n; ++i)
buildSplitImpl(s, maxThreads, thOffset, id+i*idStep, false);
}
};
auto buildSplitImpl = makeRecursiveLambda(
[&populateSplitImpl](
auto buildSplitImpl,
auto& s, auto maxThreads, auto thOffset,
auto id, bool isRemainder
) {
auto idStep = skeletonStep(s);
auto populateSplit = [&](auto& s, std::size_t n) {
if(!idStep) return;
populateSplitImpl(buildSplitImpl, s, maxThreads, thOffset, n, idStep, id, isRemainder);
};
skeletonTraversal(s, populateSplit);
}
);
buildSplitImpl(s, cores, 0ul, 0ul, false);
return ms;
}
template<typename Impl>
void buildSplit(Impl& impl) {
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
split.clear();
split.insert(0);
for(auto cores: repeatability.coresList) {
std::size_t curThread = 0;
for(auto p: buildSplitFor(impl.skeleton, cores)) { // TODO: C++17
if(std::get<1>(p) != curThread) {
curThread = std::get<1>(p);
split.insert(std::get<0>(p));
}
}
}
}
std::size_t threadLimit(Info const& info) const {
auto const& lCores = info.cores;
return lCores? lCores : cores;
}
public:
template<typename Impl>
void config(Impl& impl) {
typename Impl::State& state = impl.state;
impl.executorInfo.cores = cores;
impl.executorInfo.offset = 0;
state.executor.config(cores);
state.executor.parTasksCount = impl.parallelTasksCount();;
buildSplit(impl);
}
template<typename Impl>
std::size_t contextIdCount(Impl& impl, std::size_t) {
typename Impl::State& state = impl.state;
return state.executor.split.size();
}
template<typename Impl>
std::size_t contextId(Impl& impl, std::size_t id) { // O(log(n))
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
return std::distance(std::begin(split), split.upper_bound(id)) - 1;
}
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
std::size_t const maxThreads = threadLimit(impl.executorInfo);
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 0) {
std::vector<std::future<void>> futures(nThreads);
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
std::size_t const coresA = maxThreads/nThreads; // cores for sub tasks in main cases
std::size_t const coresB = remainBase? maxThreads/remainBase : 1; // cores for remaining tasks
typename Impl::State& state = impl.state;
auto run = [&](std::size_t b, std::size_t k, bool offset, std::size_t thOffset) {
Info infoA{coresA, thOffset}, infoB{coresB, thOffset};
std::size_t i;
for(i = 0; i < k; ++i)
Task::execute(impl, task, b+i, infoA, parameters, std::tuple<>{});
if(offset)
Task::execute(impl, task, b+i, infoB, parameters, std::tuple<>{});
};
for(std::size_t i = 0, start = 0; i < nThreads; ++i) {
std::size_t thNum = impl.executorInfo.offset + i*coresA;
std::size_t offset = !!remain;
remain -= offset;
auto task = [&run, start, step, offset, thNum]{ run(start, step, offset, thNum); };
futures[i] = state.executor.run(thNum, std::move(task));
start += step+offset;
}
state.executor.wait(futures);
} else {
Info info{impl.executorInfo};
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
std::size_t const maxThreads = threadLimit(impl.executorInfo);
Value best{};
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 0) {
std::vector<std::future<void>> futures(nThreads);
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
std::size_t const coresA = maxThreads/nThreads; // cores for sub tasks in main cases
std::size_t const coresB = remainBase? maxThreads/remainBase : 1; // cores for remaining tasks
typename Impl::State& state = impl.state;
auto run = [&](Value& out, std::size_t b, std::size_t k, bool offset, std::size_t thOffset) {
Value best{};
Info infoA{coresA, thOffset}, infoB{coresB, thOffset};
if(k) {
best = Task::execute(impl, task, b+0, infoA, parameters, std::tuple<>{});
std::size_t i;
for(i = 1; i < k; ++i) {
Value current = Task::execute(impl, task, b+i, infoA, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, infoA, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
if(offset) {
Value current = Task::execute(impl, task, b+i, infoB, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, infoB, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
out = std::move(best);
};
std::vector<Value> bests(nThreads);
for(std::size_t i = 0, start = 0; i < nThreads; ++i) {
std::size_t thNum = impl.executorInfo.offset + i*coresA;
std::size_t offset = !!remain;
remain -= offset;
auto task = [&, &best=bests[i], start, step, offset, thNum]{ run(best, start, step, offset, thNum); };
futures[i] = state.executor.run(thNum, std::move(task));
start += step+offset;
}
state.executor.wait(futures);
if(nThreads) best = std::move(bests[0]);
for(std::size_t i = 1; i < nThreads; ++i)
best = Select::execute(impl, select, i, impl.executorInfo, parameters, std::tuple<>{}, std::move(bests[i]), std::move(best));
} else {
Info info{impl.executorInfo};
if(n)
best = Task::execute(impl, task, 0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < n; ++i) {
Value current = Task::execute(impl, task, i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
return best;
}
};
template<typename S>
struct ExecutorState<StaticPool<S>>: util::StaticPool {
std::size_t parTasksCount;
std::set<std::size_t> split;
};
}
}
#endif

View File

@ -0,0 +1,110 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_STATICPOOLID_H
#define ALSK_ALSK_EXECUTOR_IMPL_STATICPOOLID_H
#include <algorithm>
#include <cmath>
#include <future>
#include <list>
#include <set>
#include <vector>
#include "../executorbase.h"
#include "../executorstate.h"
#include "../../skeleton/traits.h"
#include "../utility/staticpool.h"
namespace alsk {
namespace exec {
template<typename S>
struct StaticPoolId: ExecutorBase {
using Tag = alsk::tag::Parallel;
private:
template<typename Impl>
void buildSplit(Impl& impl) {
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
split.clear();
split.insert(0);
auto const n = static_cast<double>(state.executor.upperId);
for(auto cores: repeatability.coresList)
for(std::size_t i = 1; i < cores; ++i)
split.insert(std::ceil(n/cores * i));
}
public:
template<typename Impl>
void config(Impl& impl) {
typename Impl::State& state = impl.state;
state.executor.config(cores);
state.executor.upperId = impl.parallelTasksCount();
buildSplit(impl);
}
template<typename Impl>
std::size_t contextIdCount(Impl& impl, std::size_t) {
typename Impl::State& state = impl.state;
return state.executor.split.size();
}
template<typename Impl>
std::size_t contextId(Impl& impl, std::size_t id) { // O(log(n))
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
return std::distance(std::begin(split), split.upper_bound(id)) - 1;
}
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
std::list<std::future<void>> futures;
typename Impl::State& state = impl.state;
for(std::size_t i = 0; i < n; ++i) {
std::size_t thNum = cores * (impl.id + impl.skeleton.step * i) / state.executor.upperId;
auto thTask = [&, i]{ Task::execute(impl, task, i, Info{}, parameters, std::tuple<>{}); };
futures.emplace_back(state.executor.run(thNum, std::move(thTask)));
}
state.executor.wait(futures);
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
Value best{};
std::vector<Value> bests(n);
std::list<std::future<void>> futures;
typename Impl::State& state = impl.state;
for(std::size_t i = 0; i < n; ++i) {
std::size_t thNum = cores * (impl.id + impl.skeleton.step * i) / state.executor.upperId;
auto thTask = [&, &best = bests[i], i]{ best = Task::execute(impl, task, i, Info{}, parameters, std::tuple<>{}); };
futures.emplace_back(state.executor.run(thNum, std::move(thTask)));
}
state.executor.wait(futures);
if(n) best = std::move(bests[0]);
for(std::size_t i = 1; i < n; ++i)
best = Select::execute(impl, select, i, impl.executorInfo, parameters, std::tuple<>{}, std::move(bests[i]), std::move(best));
return best;
}
};
template<typename S>
struct ExecutorState<StaticPoolId<S>>: util::StaticPool {
std::size_t upperId;
std::set<std::size_t> split;
};
}
}
#endif

View File

@ -0,0 +1,268 @@
#ifndef ALSK_ALSK_EXECUTOR_IMPL_STATICTHREAD_H
#define ALSK_ALSK_EXECUTOR_IMPL_STATICTHREAD_H
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <thread>
#include <vector>
#include "../executorbase.h"
#include "../executorstate.h"
#include "../../skeleton/traits.h"
namespace alsk {
namespace exec {
template<typename S>
struct StaticThread: ExecutorBase {
using Tag = alsk::tag::Parallel;
public:
struct Info {
std::size_t cores;
Info(std::size_t cores = 0):
cores{cores} {}
};
private:
auto buildSplitFor(S& s, std::size_t cores) {
std::map<std::size_t, std::size_t> ms;
auto populateSplitImpl = [&ms, totalCores=cores](
auto& buildSplitImpl, auto& s,
std::size_t maxThreads, std::size_t thOffset,
std::size_t n, std::size_t idStep, std::size_t id, bool isRemainder
) {
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 0) {
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
std::size_t const coresA = maxThreads/nThreads;
std::size_t const coresB = remainBase? maxThreads/remainBase : 1;
std::size_t start = 0;
for(std::size_t i = 0; i < nThreads; ++i) {
std::size_t thNum = thOffset + i*coresA;
std::size_t offset = !!remain;
remain -= offset;
if(!ms.count(id+start*idStep))
ms[id+start*idStep] = thNum;
for(std::size_t j = 0; j < step; ++j)
buildSplitImpl(s, coresA, thNum, id+(start+j)*idStep, false);
if(offset)
buildSplitImpl(s, coresB, thNum, id+(start+step)*idStep, true);
start += step+offset;
}
if(isRemainder) ms[id+start*idStep] = totalCores;
} else {
for(std::size_t i = 0; i < n; ++i)
buildSplitImpl(s, maxThreads, thOffset, id+i*idStep, false);
}
};
auto buildSplitImpl = makeRecursiveLambda(
[&populateSplitImpl](
auto buildSplitImpl,
auto& s, auto maxThreads, auto thOffset,
auto id, bool isRemainder
) {
auto idStep = skeletonStep(s);
auto populateSplit = [&](auto& s, std::size_t n) {
if(!idStep) return;
populateSplitImpl(buildSplitImpl, s, maxThreads, thOffset, n, idStep, id, isRemainder);
};
skeletonTraversal(s, populateSplit);
}
);
buildSplitImpl(s, cores, 0ul, 0ul, false);
return ms;
}
template<typename Impl>
void buildSplit(Impl& impl) {
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
split.clear();
split.insert(0);
for(auto cores: repeatability.coresList) {
std::size_t curThread = 0;
for(auto p: buildSplitFor(impl.skeleton, cores)) { // TODO: C++17
if(std::get<1>(p) != curThread) {
curThread = std::get<1>(p);
split.insert(std::get<0>(p));
}
}
}
}
std::size_t threadLimit(Info const& info) const {
auto const& lCores = info.cores;
return lCores? lCores : cores;
}
public:
template<typename Impl>
void config(Impl& impl) {
typename Impl::State& state = impl.state;
impl.executorInfo.cores = cores;
state.executor.parTasksCount = impl.parallelTasksCount();;
buildSplit(impl);
}
template<typename Impl>
std::size_t contextIdCount(Impl& impl, std::size_t) {
typename Impl::State& state = impl.state;
return state.executor.split.size();
}
template<typename Impl>
std::size_t contextId(Impl& impl, std::size_t id) { // O(log(n))
typename Impl::State& state = impl.state;
auto& split = state.executor.split;
return std::distance(std::begin(split), split.upper_bound(id)) - 1;
}
template<typename Task, typename Impl, typename BTask, typename Parameters>
void executeParallel(Impl& impl, BTask& task, Parameters const& parameters, std::size_t n) {
std::size_t const maxThreads = threadLimit(impl.executorInfo);
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 1) {
std::vector<std::thread> threads(nThreads-1);
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
std::size_t const coresA = maxThreads/nThreads; // cores for sub tasks in main cases
std::size_t const coresB = remainBase? maxThreads/remainBase : 1; // cores for remaining tasks
auto run = [&](std::size_t b, std::size_t k, bool offset = false) {
Info infoA{coresA}, infoB{coresB};
std::size_t i;
for(i = 0; i < k; ++i)
Task::execute(impl, task, b+i, infoA, parameters, std::tuple<>{});
if(offset)
Task::execute(impl, task, b+i, infoB, parameters, std::tuple<>{});
};
{
std::size_t start = 0;
for(std::size_t i = 0; i < nThreads-1; ++i) {
std::size_t offset = !!remain;
remain -= offset;
auto task = [&run, start, step, offset]{ run(start, step, offset); };
threads[i] = std::thread{std::move(task)};
start += step+offset;
}
run(start, step);
}
for(std::thread& thread: threads) thread.join();
} else {
Info info{impl.executorInfo};
for(std::size_t i = 0; i < n; ++i)
Task::execute(impl, task, i, info, parameters, std::tuple<>{});
}
}
template<typename Value, typename Task, typename Select, typename Impl, typename BTask, typename BSelect, typename Parameters>
Value executeParallelAccumulate(Impl& impl, BTask& task, BSelect& select, Parameters const& parameters, std::size_t n) {
std::size_t const maxThreads = threadLimit(impl.executorInfo);
Value best{};
std::size_t const nThreads = std::min(n, maxThreads);
if(nThreads > 1) {
std::vector<std::thread> threads(nThreads-1);
std::size_t const step = n/nThreads;
std::size_t const remainBase = n - step*nThreads;
std::size_t remain = remainBase;
std::size_t const coresA = maxThreads/nThreads; // cores for sub tasks in main cases
std::size_t const coresB = remainBase? maxThreads/remainBase : 1; // cores for remaining tasks
auto run = [&](Value& out, std::size_t b, std::size_t k, bool offset = false) {
Value best{};
Info infoA{coresA}, infoB{coresB};
if(k) {
best = Task::execute(impl, task, b+0, infoA, parameters, std::tuple<>{});
std::size_t i;
for(i = 1; i < k; ++i) {
Value current = Task::execute(impl, task, b+i, infoA, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, infoA, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
if(offset) {
Value current = Task::execute(impl, task, b+i, infoB, parameters, std::tuple<>{});
best = Select::execute(impl, select, b+i, infoB, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
out = std::move(best);
};
std::vector<Value> bests(nThreads);
{
std::size_t start = 0;
for(std::size_t i = 0; i < nThreads-1; ++i) {
std::size_t offset = !!remain;
remain -= offset;
auto task = [&, &best=bests[i], start, step, offset]{ run(best, start, step, offset); };
threads[i] = std::thread{std::move(task)};
start += step+offset;
}
run(bests[nThreads-1], start, step);
}
for(std::thread& thread: threads) thread.join();
if(nThreads) best = std::move(bests[0]);
for(std::size_t i = 1; i < nThreads; ++i)
best = Select::execute(impl, select, i, impl.executorInfo, parameters, std::tuple<>{}, std::move(bests[i]), std::move(best));
} else {
Info info{impl.executorInfo};
if(n)
best = Task::execute(impl, task, 0, info, parameters, std::tuple<>{});
for(std::size_t i = 1; i < n; ++i) {
Value current = Task::execute(impl, task, i, info, parameters, std::tuple<>{});
best = Select::execute(impl, select, i, info, parameters, std::tuple<>{}, std::move(current), std::move(best));
}
}
return best;
}
};
template<typename S>
struct ExecutorState<StaticThread<S>> {
std::size_t parTasksCount;
std::set<std::size_t> split;
};
}
}
#endif

14
inc/alsk/executor/tags.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef ALSK_ALSK_EXECUTOR_TAGS_H
#define ALSK_ALSK_EXECUTOR_TAGS_H
namespace alsk {
namespace exec {
namespace tag {
struct Executor {};
}
}
}
#endif

View File

@ -0,0 +1,20 @@
#ifndef ALSK_ALSK_EXECUTOR_TRAITS_H
#define ALSK_ALSK_EXECUTOR_TRAITS_H
#include <type_traits>
#include "executorbase.h"
namespace alsk {
template<typename, typename=void> struct IsExecutorImpl: std::false_type {};
template<typename T>
struct IsExecutorImpl<T, std::enable_if_t<std::is_same<typename std::decay_t<T>::IsExecutor, exec::tag::Executor>{}>>: std::true_type {};
template<typename T>
constexpr bool isExecutor = IsExecutorImpl<T>::value;
}
#endif

View File

@ -0,0 +1,154 @@
#ifndef ALSK_ALSK_EXECUTOR_UTILITY_POOL_H
#define ALSK_ALSK_EXECUTOR_UTILITY_POOL_H
#include <atomic>
#include <condition_variable>
#include <functional>
#include <future>
#include <list>
#include <mutex>
#include <vector>
#include <tmp/traits.h>
namespace alsk {
namespace exec {
namespace util {
struct Pool {
using Task = std::function<void()>;
using TaskInfo = std::tuple<Task, std::promise<void>>;
private:
std::atomic_bool _running;
std::vector<std::thread> _threads;
std::list<TaskInfo> _tasks;
std::mutex _mutexTasks;
std::condition_variable _cvTasks;
std::mutex _mutexProcessed;
std::condition_variable _cvProcessed;
public:
Pool(): _running{true} {}
Pool(Pool const& o): _running{true} {
config(o._threads.size());
}
Pool(Pool&& o): _running{true} {
config(o._threads.size());
}
~Pool() {
terminate();
}
Pool const& operator=(Pool const& o) {
if(this == &o) return *this;
config(o._threads.size());
return *this;
}
Pool const& operator=(Pool&& o) {
if(this == &o) return *this;
config(o._threads.size());
return *this;
}
void config(unsigned int cores) {
terminate();
_running = true;
if(cores == 0) return;
--cores; // main thread will work too
_threads.reserve(cores);
while(cores--)
_threads.emplace_back([&]{ worker(); });
}
template<typename F, typename R = tmp::invoke_result_t<F>, std::enable_if_t<std::is_same<R, void>{}>* = nullptr>
std::future<void> run(F&& task) {
std::future<void> future;
{
std::lock_guard<std::mutex> lg{_mutexTasks};
_tasks.emplace_back(std::forward<F>(task), std::promise<void>{});
future = std::get<1>(_tasks.back()).get_future();
}
_cvTasks.notify_one();
return future;
}
template<typename F, typename R = tmp::invoke_result_t<F>, std::enable_if_t<not std::is_same<R, void>{}>* = nullptr>
std::future<R> run(F&& task, std::promise<R>& promise) {
std::future<R> future = promise.get_future();
run([task=std::forward<F>(task), &promise]{ promise.set_value(task()); });
return future;
}
template<typename Futures>
void wait(Futures& futures) {
while(tryProcessOne());
for(auto& future: futures) future.wait();
}
protected:
void terminate() {
{
std::lock_guard<std::mutex> lk{_mutexTasks};
_running = false;
}
_cvTasks.notify_all();
for(auto& thread: _threads) thread.join();
_threads.clear();
}
void worker() {
auto test = [&]{ return !_running || _tasks.size(); };
for(;;) {
TaskInfo taskInfo;
{
std::unique_lock<std::mutex> lk{_mutexTasks};
if(!test()) _cvTasks.wait(lk, test);
if(!_running) return;
taskInfo = std::move(_tasks.front());
_tasks.pop_front();
}
process(taskInfo);
}
}
bool tryProcessOne() {
TaskInfo taskInfo;
{
std::unique_lock<std::mutex> lk{_mutexTasks};
if(_tasks.empty()) return false;
taskInfo = std::move(_tasks.front());
_tasks.pop_front();
}
process(taskInfo);
return true;
}
void process(TaskInfo& taskInfo) {
std::get<0>(taskInfo)();
std::get<1>(taskInfo).set_value();
_cvProcessed.notify_all();
}
};
}
}
}
#endif

View File

@ -0,0 +1,161 @@
#ifndef ALSK_ALSK_EXECUTOR_UTILITY_STATICPOOL_H
#define ALSK_ALSK_EXECUTOR_UTILITY_STATICPOOL_H
#include <atomic>
#include <condition_variable>
#include <functional>
#include <future>
#include <list>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#include <tmp/traits.h>
namespace alsk {
namespace exec {
namespace util {
struct StaticPool {
using Task = std::function<void()>;
using TaskInfo = std::tuple<Task, std::promise<void>>;
struct ThreadInfo {
std::atomic_bool running;
std::thread thread;
std::list<TaskInfo> tasks;
std::mutex mutex;
std::condition_variable cv;
ThreadInfo() {}
ThreadInfo(ThreadInfo&&) {}
};
private:
std::vector<ThreadInfo> _threads;
std::unordered_map<std::thread::id, std::reference_wrapper<ThreadInfo>> _threadFromId;
public:
StaticPool() {}
StaticPool(StaticPool const& o) {
config(o._threads.size());
}
StaticPool(StaticPool&& o) {
config(o._threads.size());
}
~StaticPool() {
terminate();
}
StaticPool const& operator=(StaticPool const& o) {
if(this == &o) return *this;
config(o._threads.size());
return *this;
}
StaticPool const& operator=(StaticPool&& o) {
if(this == &o) return *this;
config(o._threads.size());
return *this;
}
void config(unsigned int cores) {
terminate();
if(cores == 0) return;
_threads.resize(cores);
for(unsigned int i = 0; i < cores; ++i) {
ThreadInfo& threadInfo = _threads[i];
threadInfo.running = true;
threadInfo.thread = std::thread{[&,&threadInfo=threadInfo] { worker(threadInfo); }};
_threadFromId.emplace(threadInfo.thread.get_id(), threadInfo);
}
}
template<typename F, typename R = tmp::invoke_result_t<F>, std::enable_if_t<std::is_same<R, void>{}>* = nullptr>
std::future<void> run(std::size_t i, F&& task) {
ThreadInfo& threadInfo = _threads[i];
std::future<void> future;
{
std::lock_guard<std::mutex> lg{threadInfo.mutex};
threadInfo.tasks.emplace_back(std::forward<F>(task), std::promise<void>{});
future = std::get<1>(threadInfo.tasks.back()).get_future();
}
threadInfo.cv.notify_one();
return future;
}
template<typename Futures>
void wait(Futures& futures) {
auto const& id = std::this_thread::get_id();
if(_threadFromId.count(id)) {
auto& threadInfo = _threadFromId.at(id);
while(tryProcessOne(threadInfo));
}
for(auto& future: futures) future.wait();
futures.clear();
}
protected:
void terminate() {
for(auto& threadInfo: _threads) {
{
std::lock_guard<std::mutex> lg{threadInfo.mutex};
threadInfo.running = false;
}
threadInfo.cv.notify_all();
threadInfo.thread.join();
}
_threads.clear();
_threadFromId.clear();
}
void worker(ThreadInfo& threadInfo) {
auto test = [&]{ return !threadInfo.running || threadInfo.tasks.size(); };
for(;;) {
TaskInfo taskInfo;
{
std::unique_lock<std::mutex> lk{threadInfo.mutex};
if(!test()) threadInfo.cv.wait(lk, test);
if(!threadInfo.running) return;
taskInfo = std::move(threadInfo.tasks.front());
threadInfo.tasks.pop_front();
}
process(taskInfo);
}
}
bool tryProcessOne(ThreadInfo& threadInfo) {
TaskInfo taskInfo;
{
std::unique_lock<std::mutex> lk{threadInfo.mutex};
if(threadInfo.tasks.empty()) return false;
taskInfo = std::move(threadInfo.tasks.front());
threadInfo.tasks.pop_front();
}
process(taskInfo);
return true;
}
void process(TaskInfo& taskInfo) {
std::get<0>(taskInfo)();
std::get<1>(taskInfo).set_value();
}
};
}
}
}
#endif

75
inc/alsk/impl/bone/farm.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef ALSK_ALSK_IMPL_BONE_FARM_H
#define ALSK_ALSK_IMPL_BONE_FARM_H
#include <cmath>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
#include "../boneimplbase.h"
#include "../../skeleton/bone/farm.h"
namespace alsk {
/**
* @brief Farm implementation for sequential execution
*/
template<typename R, typename... Args, typename TTask, typename Executor, typename State>
struct Impl<Farm<R(Args...), TTask>, tag::Sequential, Executor, State>:
BoneImplBase<Farm<R(Args...), TTask>, tag::Sequential, Executor, State>
{
using This = Impl;
using Task = Execute<typename This::Skeleton::TaskLinks>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
constexpr Impl() = default;
template<typename S>
constexpr Impl(S&& skeleton, Executor executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{executor},
state{state}
{}
constexpr void operator()(Args... args) {
executor.template executeSequential<Task>(
*this, skeleton.task, std::forward_as_tuple(args...), skeleton.n
);
}
};
/**
* @brief Farm implementation for parallel execution
*/
template<typename R, typename... Args, typename TTask, typename Executor, typename State>
struct Impl<Farm<R(Args...), TTask>, tag::Parallel, Executor, State>:
BoneImplBase<Farm<R(Args...), TTask>, tag::Parallel, Executor, State>
{
using This = Impl;
using Task = Execute<typename This::Skeleton::TaskLinks>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
constexpr Impl() = default;
template<typename S>
constexpr Impl(S&& skeleton, Executor executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{executor},
state{state}
{}
constexpr void operator()(Args... args) {
executor.template executeParallel<Task>(
*this, skeleton.task, std::forward_as_tuple(args...), skeleton.n
);
}
};
}
#endif

View File

@ -0,0 +1,91 @@
#ifndef ALSK_ALSK_IMPL_BONE_FARMSEL_H
#define ALSK_ALSK_IMPL_BONE_FARMSEL_H
#include <cmath>
#include <thread>
#include <vector>
#include "../boneimplbase.h"
#include "../../skeleton/bone/farmsel.h"
namespace alsk {
/**
* @brief FarmSel implementation for sequential execution
*/
/*
template<typename R, typename... Args, typename... Tasks, typename Executor, typename State>
struct Impl<FarmSel<R(Args...), Tasks...>, tag::Sequential, Executor, State>:
BoneImplBase<FarmSel<R(Args...), Tasks...>, tag::Sequential, Executor, State>
{
using This = Impl;
using Task = Execute<typename This::Skeleton::TaskLinks>;
using Select = Execute<typename This::Skeleton::SelectLinks>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
constexpr Impl() = default;
template<typename S>
constexpr Impl(S&& skeleton, Executor executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{executor},
state{state}
{}
constexpr typename This::Return operator()(Args... args) {
using Value = typename This::Return;
auto tupleP = std::forward_as_tuple(args...);
Value best{};
if(skeleton.n)
best = executor.template execute<Task>(*this, skeleton.task, tupleP, std::tuple<>{});
for(std::size_t i = 1; i < skeleton.n; ++i) {
Value current = executor.template execute<Task>(*this, skeleton.task, tupleP, std::tuple<>{});
best = executor.template execute<Select>(
*this, skeleton.select, tupleP, std::tuple<>{}, std::move(current), std::move(best)
);
}
return best;
}
};
*/
/**
* @brief FarmSel implementation for parallel execution
*/
template<typename R, typename... Args, typename... Tasks, typename Tag, typename Executor, typename State>
struct Impl<FarmSel<R(Args...), Tasks...>, Tag, Executor, State>:
BoneImplBase<FarmSel<R(Args...), Tasks...>, Tag, Executor, State>
{
using This = Impl;
using Task = Execute<typename This::Skeleton::TaskLinks>;
using Select = Execute<typename This::Skeleton::SelectLinks>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
constexpr Impl() = default;
template<typename S>
constexpr Impl(S&& skeleton, Executor executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{executor},
state{state}
{}
constexpr typename This::Return operator()(Args... args) {
auto tupleP = std::forward_as_tuple(args...);
return executor.template executeParallelAccumulate<typename This::Return, Task, Select>(
*this, skeleton.task, skeleton.select, tupleP, skeleton.n
);
}
};
}
#endif

View File

@ -0,0 +1,57 @@
#ifndef ALSK_ALSK_IMPL_BONE_ITERSEL_H
#define ALSK_ALSK_IMPL_BONE_ITERSEL_H
#include <cmath>
#include <thread>
#include <vector>
#include "../boneimplbase.h"
#include "../../skeleton/bone/itersel.h"
namespace alsk {
/**
* @brief IterSel implementation for any execution
*/
template<typename R, typename... Args, typename... Tasks, typename Tag, typename Executor, typename State>
struct Impl<IterSel<R(Args...), Tasks...>, Tag, Executor, State>:
BoneImplBase<IterSel<R(Args...), Tasks...>, Tag, Executor, State>
{
using This = Impl;
using Task = Execute<typename This::Skeleton::TaskLinks>;
using Select = Execute<typename This::Skeleton::SelectLinks>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
constexpr Impl() = default;
template<typename S, typename O>
constexpr Impl(S&& skeleton, O&& executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{std::forward<O>(executor)},
state{state}
{}
// TODO check repeatability with tag::Parallel
constexpr typename This::Return operator()(Args... args) {
using Value = typename This::Return;
auto tupleP = std::forward_as_tuple(args...);
Value best = ArgGet<arg::P<0>>::get(tupleP, std::tuple<>{}, std::tuple<>{});
for(std::size_t i = 0; i < skeleton.n; ++i) {
Value current = executor.template execute<Task>(*this, skeleton.task, tupleP, std::tuple<>{}, best);
best = executor.template execute<Select>(
*this, skeleton.select, tupleP, std::tuple<>{}, std::move(current), std::move(best)
);
}
return best;
}
};
}
#endif

41
inc/alsk/impl/bone/loop.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef ALSK_ALSK_IMPL_BONE_LOOP_H
#define ALSK_ALSK_IMPL_BONE_LOOP_H
#include <tuple>
#include "../boneimplbase.h"
#include "../../skeleton/bone/loop.h"
namespace alsk {
/**
* @brief Loop implementation for any execution
*/
template<typename R, typename... Args, typename Task, typename Tag, typename Executor, typename State>
struct Impl<Loop<R(Args...), Task>, Tag, Executor, State>:
BoneImplBase<Loop<R(Args...), Task>, Tag, Executor, State>
{
using This = Impl;
using ETask = Execute<typename This::Skeleton::TaskLinks>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
constexpr Impl() = default;
template<typename S, typename O>
constexpr Impl(S&& skeleton, O&& executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{std::forward<O>(executor)},
state{state}
{}
constexpr typename This::Return operator()(Args... args) {
for(unsigned int i = 0; i < skeleton.n; ++i)
executor.template execute<ETask>(*this, skeleton.task, std::forward_as_tuple(args...), std::tuple<>{});
}
};
}
#endif

View File

@ -0,0 +1,82 @@
#ifndef ALSK_ALSK_IMPL_BONE_SERIAL_H
#define ALSK_ALSK_IMPL_BONE_SERIAL_H
#include "../boneimplbase.h"
#include "../../skeleton/bone/serial.h"
namespace alsk {
/**
* @brief Serial implementation for any execution
*/
template<typename R, typename... Args, typename... Tasks, typename Tag, typename Executor, typename State>
struct Impl<Serial<R(Args...), Tasks...>, Tag, Executor, State>:
BoneImplBase<Serial<R(Args...), Tasks...>, Tag, Executor, State>
{
using This = Impl;
using Results = tmp::Repack<std::tuple, tmp::PackReplace<typename This::Skeleton::PackR, void, tmp::Void>>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
Results results;
constexpr Impl() = default;
template<typename S, typename O>
constexpr Impl(S&& skeleton, O&& executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{std::forward<O>(executor)},
state{state}
{}
// out-of-class definition to silence -Winline
~Impl() noexcept;
// TODO: what happens with rvalue references?
constexpr typename This::Return operator()(Args... args) {
return serial(std::forward_as_tuple(args...));
}
private:
template<typename TupleP>
constexpr decltype(auto) serial(TupleP&& p) {
serial<0, typename This::Skeleton::Links>(skeleton.tasks, p);
return ArgGet<R>::get(std::tuple<>{}, std::move(results), std::tuple<>{});
}
/**
* execute
*/
template<
std::size_t I, typename LinksPack, typename TupleTasks,
typename TupleP,
std::enable_if_t<(I < std::tuple_size<std::decay_t<TupleTasks>>::value)>* = nullptr
>
constexpr void serial(TupleTasks& tasks, TupleP& p) {
using Task = Execute<tmp::PackHead<LinksPack>>;
std::get<I>(results) = executor.template execute<Task>(
*this, std::get<I>(tasks), p, results
);
serial<I+1, tmp::PackTrail<LinksPack>>(tasks, p);
}
/**
* recursion terminaison
*/
template<
std::size_t I, typename LinksPack, typename TupleTasks,
typename TupleP,
std::enable_if_t<(I == std::tuple_size<std::decay_t<TupleTasks>>::value)>* = nullptr
>
constexpr void serial(TupleTasks&&, TupleP&&) noexcept {}
};
// out-of-class definition to silence -Winline
template<typename R, typename... Args, typename... Tasks, typename Tag, typename Executor, typename State>
Impl<Serial<R(Args...), Tasks...>, Tag, Executor, State>::~Impl() noexcept {}
}
#endif

View File

@ -0,0 +1,46 @@
#ifndef ALSK_ALSK_IMPL_BONE_WHILE_H
#define ALSK_ALSK_IMPL_BONE_WHILE_H
#include <cmath>
#include <vector>
#include "../boneimplbase.h"
#include "../../skeleton/bone/while.h"
namespace alsk {
/**
* @brief While implementation for any execution
*/
template<typename R, typename... Args, typename... Tasks, typename Tag, typename Executor, typename State>
struct Impl<While<R(Args...), Tasks...>, Tag, Executor, State>:
BoneImplBase<While<R(Args...), Tasks...>, Tag, Executor, State>
{
using This = Impl;
using Test = Execute<typename This::Skeleton::TestLinks>;
using Task = Execute<typename This::Skeleton::TaskLinks>;
typename This::Skeleton skeleton;
typename This::Executor executor;
typename This::StateRef state;
constexpr Impl() = default;
template<typename S, typename O>
constexpr Impl(S&& skeleton, O&& executor, State& state):
skeleton{std::forward<S>(skeleton)},
executor{std::forward<O>(executor)},
state{state}
{}
template<typename T>
constexpr typename This::Return operator()(T& a, T const& b) {
auto tupleP = std::forward_as_tuple(a, b);
while(executor.template execute<Test>(*this, skeleton.test, tupleP, std::tuple<>{}))
executor.template execute<Task>(*this, skeleton.task, tupleP, std::tuple<>{});
}
};
}
#endif

View File

@ -0,0 +1,40 @@
#ifndef ALSK_ALSK_IMPL_BONEIMPLBASE_H
#define ALSK_ALSK_IMPL_BONEIMPLBASE_H
#include <functional>
#include "execute.h"
#include "tags.h"
#include "../utility.h"
namespace alsk {
template<typename, typename, typename, typename> struct BoneImplBase;
template<
template<typename...> class Bone,
typename R, typename... Args, typename... Tasks,
typename Tag_, typename Executor_, typename State_
>
struct BoneImplBase<Bone<R(Args...), Tasks...>, Tag_, Executor_, State_> {
using Signature = R(Args...);
using Skeleton = Bone<Signature, Tasks...>;
using Tag = Tag_;
using Executor = Executor_;
using ExecutorInfo = typename std::decay_t<Executor_>::Info;
using State = State_;
using StateRef = std::reference_wrapper<State>;
using Return = RealType<R, typename Skeleton::Packs>;
ExecutorInfo executorInfo;
constexpr BoneImplBase() noexcept {}
std::size_t id = 0;
};
}
#endif

89
inc/alsk/impl/callable.h Normal file
View File

@ -0,0 +1,89 @@
#ifndef ALSK_ALSK_IMPL_CALLABLE_H
#define ALSK_ALSK_IMPL_CALLABLE_H
#include "../skeleton/utility.h"
#include "../executor/executorstate.h"
namespace alsk {
template<typename Context, typename Executor>
struct CallableState {
Context context;
exec::ExecutorState<Executor> executor;
// out-of-class definition to silence -Winline
~CallableState() noexcept;
};
template<typename Context, typename Executor>
CallableState<Context, Executor>::~CallableState() noexcept {}
template<typename> struct Callable;
template<
template<typename...> class Impl,
template<typename...> class Skel, typename R, typename... Operands, typename... Tasks,
typename Tag, typename Executor, typename State
>
struct Callable<Impl<Skel<R(Operands...), Tasks...>, Tag, Executor, State>>:
Impl<Skel<R(Operands...), Tasks...>, Tag, Executor, State> {
using Skeleton = Skel<R(Operands...), Tasks...>;
using Implementation = Impl<Skeleton, Tag, Executor, State>;
using ThisType = Callable<Implementation>;
private:
std::size_t _parTasksCount;
public:
State state;
template<typename... Args, std::enable_if_t<sizeof...(Args) == 2>* = nullptr>
constexpr Callable(Args&&... args):
Implementation(std::forward<Args>(args)..., state),
_parTasksCount{0}
{}
// out-of-class definition to silence -Winline
~Callable() noexcept;
constexpr decltype(auto) operator()(Operands... operands) {
update();
return Implementation::operator()(operands...);
}
template<typename F, typename T>
constexpr decltype(auto) traverse(F&& function, T&& init) {
return SkeletonTraversal<Skeleton>::execute(Implementation::skeleton, std::forward<F>(function), std::forward<T>(init));
}
static constexpr tmp::Depth parallelizableLevels() {
return skeletonParallelHeight<Skeleton>;
}
std::size_t parallelTasksCount() const { return _parTasksCount; }
constexpr void update() {
auto traverser = [](std::size_t, auto&& skl, auto&&... values) {
using Skl = decltype(skl);
auto subpar = max(values...);
skl.step = subpar;
return SkeletonTraitsT<Skl>::parallelizability(std::forward<Skl>(skl)) * subpar;
};
_parTasksCount = traverse(traverser, 1ul);
ThisType::executor.config(*this);
state.context.setup(ThisType::executor.contextIdCount(static_cast<Implementation&>(*this), _parTasksCount));
}
};
template<
template<typename...> class Impl,
template<typename...> class Skel, typename R, typename... Operands, typename... Tasks,
typename Tag, typename Executor, typename State
>
Callable<Impl<Skel<R(Operands...), Tasks...>, Tag, Executor, State>>::~Callable() noexcept {}
}
#endif

118
inc/alsk/impl/execute.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef ALSK_ALSK_IMPL_EXECUTE_H
#define ALSK_ALSK_IMPL_EXECUTE_H
#include "../skeleton/utility.h"
#include "../executor/traits.h"
namespace alsk {
namespace impl {
/**
* @brief internal implement function
* @param executor an instance of the selected executor
* @param skeleton an instance of the skeleton to implement
* @param state the shared state
* @param id the task identifier
* @param eInfo information provided by the executor
*/
template<
typename Executor, typename S, typename State, typename CtxId, typename ExecutorInfo,
std::enable_if_t<isSkeleton<S>>* = nullptr,
std::enable_if_t<isExecutor<Executor>>* = nullptr
>
auto implement(Executor& executor, S&& skeleton, State&& state, CtxId&& id, ExecutorInfo&& eInfo) {
using ImplType = Impl<std::decay_t<S>, typename Executor::Tag, Executor&, std::remove_reference_t<State>>;
auto impl = ImplType{std::forward<S>(skeleton), executor, std::forward<State>(state)};
impl.id = std::forward<CtxId>(id);
impl.executorInfo = std::forward<ExecutorInfo>(eInfo);
return impl;
}
/**
* @brief internal implement function for end muscles
* @param executor ignored
* @param value a muscle
* @param state ignored
* @param id ignored
* @param eInfo ignored
*/
template<typename Executor, typename T, typename State, typename CtxId, typename ExecutorInfo,
std::enable_if_t<not isSkeleton<T>>* = nullptr,
std::enable_if_t<isExecutor<Executor>>* = nullptr
>
decltype(auto) implement(Executor&, T&& value, State&&, CtxId&&, ExecutorInfo&&) {
return std::forward<T>(value);
}
}
/**
* Execute
*/
namespace impl {
template<typename> struct Execute {};
template<typename Ret, typename... Ps>
struct Execute<Ret(Ps...)> {
template<
typename Impl, typename Task, typename CtxId, typename ExecutorInfo,
typename P, typename R, typename... Args
>
static decltype(auto) execute(Impl& impl, Task&& task, CtxId&& cid, ExecutorInfo&& eInfo, P&& p, R&& r, Args&&... args) {
auto id = impl.id + std::forward<CtxId>(cid) * impl.skeleton.step;
typename Impl::State& state = impl.state;
return executeF(
impl::implement(impl.executor, std::forward<Task>(task), state, id, std::forward<ExecutorInfo>(eInfo)),
std::forward<P>(p), std::forward<R>(r),
state.context.args(impl.executor.contextId(impl, id)),
std::forward<Args>(args)...
);
}
private:
template<
typename F, typename P, typename R, typename C, typename... Args,
typename Result = tmp::invoke_result_t<
std::decay_t<F>,
Args...,
ArgType<Ps, arg::Placeholders<P, R, C>>...
>,
std::enable_if_t<not std::is_same<Result, void>::value>* = nullptr
>
static Result executeF(F&& f, P&& p, R&& r, C&& c, Args&&... args) {
return std::forward<F>(f)(
std::forward<Args>(args)...,
ArgGet<Ps>::get(std::forward<P>(p), std::forward<R>(r), std::forward<C>(c))...
);
}
template<
typename F, typename P, typename R, typename C, typename... Args,
typename Result = tmp::invoke_result_t<
std::decay_t<F>,
Args...,
ArgType<Ps, arg::Placeholders<P, R, C>>...
>,
std::enable_if_t<std::is_same<Result, void>::value>* = nullptr
>
static auto executeF(F&& f, P&& p, R&& r, C&& c, Args&&... args) {
std::forward<F>(f)(
std::forward<Args>(args)...,
ArgGet<Ps>::get(std::forward<P>(p), std::forward<R>(r), std::forward<C>(c))...
);
return tmp::Void{};
}
};
}
template<typename Signature>
using Execute = impl::Execute<ArgFilter<Signature>>;
}
#endif

13
inc/alsk/impl/impl.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef ALSK_ALSK_IMPL_IMPL_H
#define ALSK_ALSK_IMPL_IMPL_H
#include "bone/farm.h"
#include "bone/farmsel.h"
#include "bone/itersel.h"
#include "bone/loop.h"
#include "bone/serial.h"
#include "bone/while.h"
#include "implement.h"
#endif

57
inc/alsk/impl/implement.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef ALSK_ALSK_IMPL_IMPLEMENT_H
#define ALSK_ALSK_IMPL_IMPLEMENT_H
#include <utility>
#include "callable.h"
#include "../context/context.h"
#include "../executor/traits.h"
namespace alsk {
/**
* @brief implement a skeleton
* @param TExecutor the executor template
* @param Skeleton the skeleton type
* @param Context [optional] the type of the context
*
* @return a callable
*/
template<template<typename> class TExecutor, typename Skeleton,
typename Context = DefaultContext,
std::enable_if_t<isSkeleton<Skeleton>>* = nullptr,
std::enable_if_t<isExecutor<TExecutor<Skeleton>>>* = nullptr
>
auto implement() {
using Executor = TExecutor<Skeleton>;
using State = CallableState<Context, Executor>;
using ImplType = Impl<Skeleton, typename Executor::Tag, Executor, State>;
return Callable<ImplType>(Skeleton{}, Executor{});
}
/**
* @brief implement a skeleton
* @param executor an instance of the executor
* @param skeleton an instance of the skeleton to implement
* @param Context [optional] the type of the context
*
* @return a callable
*/
template<typename Context = DefaultContext,
typename Executor, typename S,
std::enable_if_t<isSkeleton<S>>* = nullptr,
std::enable_if_t<isExecutor<Executor>>* = nullptr
>
auto implement(Executor&& executor, S&& skeleton) {
using ExecutorT = std::decay_t<Executor>;
using State = CallableState<Context, ExecutorT>;
using ImplType = Impl<std::decay_t<S>, typename ExecutorT::Tag, ExecutorT, State>;
return Callable<ImplType>(std::forward<S>(skeleton), std::forward<Executor>(executor));
}
}
#endif

13
inc/alsk/impl/tags.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef ALSK_ALSK_IMPL_TAGS_H
#define ALSK_ALSK_IMPL_TAGS_H
namespace alsk {
namespace tag {
struct Sequential {};
struct Parallel {};
}
}
#endif

View File

@ -0,0 +1,51 @@
#ifndef ALSK_ALSK_SKELETON_BONE_FARM_H
#define ALSK_ALSK_SKELETON_BONE_FARM_H
#include "../bonebase.h"
namespace alsk {
template<typename, typename> struct Farm;
template<
typename Signature,
typename Task_, typename TaskLinks_
>
struct Farm<Signature, tmp::Pack<Task_, TaskLinks_>>: SkeletonBase {
using Task = Task_;
using TaskLinks = TaskLinks_;
using Links = tmp::Pack<TaskLinks>;
using PackP = tmp::Parameters<Signature>;
using PackR = Returns<Links>;
using PackX = tmp::Pack<>;
using Packs = arg::Placeholders<PackP, PackR, PackX>;
Task task;
std::size_t n;
};
template<>
struct SkeletonTraits<Farm> {
static constexpr bool serial = false;
template<typename Skeleton, typename F, typename T>
static constexpr decltype(auto) traverse(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init) {
using Traverser = SkeletonTraversal<typename std::decay_t<Skeleton>::Task>;
return function(parDepth, skeleton, Traverser::execute(parDepth+1, skeleton.task, function, std::forward<T>(init)));
}
template<typename Skeleton>
static constexpr std::size_t parallelizability(Skeleton&& skeleton) noexcept {
return std::forward<Skeleton>(skeleton).n;
}
};
template<typename... Args, typename F>
void skeletonTraversal(Farm<Args...> const& s, F&& f) {
std::forward<F>(f)(s.task, s.n);
}
}
#endif

View File

@ -0,0 +1,60 @@
#ifndef ALSK_ALSK_SKELETON_BONE_FARMSEL_H
#define ALSK_ALSK_SKELETON_BONE_FARMSEL_H
#include "../bonebase.h"
namespace alsk {
template<typename, typename, typename> struct FarmSel;
template<
typename Signature,
typename Task_, typename TaskLinks_,
typename Select_, typename SelectLinks_
>
struct FarmSel<Signature, tmp::Pack<Task_, TaskLinks_>, tmp::Pack<Select_, SelectLinks_>>: SkeletonBase {
using Task = Task_;
using Select = Select_;
using TaskLinks = TaskLinks_;
using SelectLinks = SelectLinks_;
using Links = tmp::Pack<TaskLinks, SelectLinks>;
using PackP = tmp::Parameters<Signature>;
using PackR = Returns<Links>;
using PackX = tmp::Pack<>;
using Packs = arg::Placeholders<PackP, PackR, PackX>;
Task task;
Select select;
std::size_t n;
};
template<>
struct SkeletonTraits<FarmSel> {
static constexpr bool serial = false;
template<typename Skeleton, typename F, typename T>
static constexpr decltype(auto) traverse(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init) {
using TraverserTask = SkeletonTraversal<typename std::decay_t<Skeleton>::Task>;
using TraverserSelect = SkeletonTraversal<typename std::decay_t<Skeleton>::Select>;
return function(parDepth, skeleton,
TraverserTask::execute(parDepth+1, skeleton.task, function, std::forward<T>(init)),
TraverserSelect::execute(parDepth, skeleton.select, function, std::forward<T>(init))
);
}
template<typename Skeleton>
static constexpr std::size_t parallelizability(Skeleton&& skeleton) noexcept {
return std::forward<Skeleton>(skeleton).n;
}
};
template<typename... Args, typename F>
void skeletonTraversal(FarmSel<Args...> const& s, F f) {
f(s.task, s.n);
f(s.select, 1);
}
}
#endif

View File

@ -0,0 +1,61 @@
#ifndef ALSK_ALSK_SKELETON_BONE_ITERSEL_H
#define ALSK_ALSK_SKELETON_BONE_ITERSEL_H
#include "../bonebase.h"
namespace alsk {
template<typename, typename, typename> struct IterSel;
template<
typename Signature,
typename Task_, typename TaskLinks_,
typename Select_, typename SelectLinks_
>
struct IterSel<Signature, tmp::Pack<Task_, TaskLinks_>, tmp::Pack<Select_, SelectLinks_>>: SkeletonBase {
using Task = Task_;
using TaskLinks = TaskLinks_;
using Select = Select_;
using SelectLinks = SelectLinks_;
using Links = tmp::Pack<TaskLinks, SelectLinks>;
using PackP = tmp::Parameters<Signature>;
using PackR = Returns<Links>;
using PackX = tmp::Pack<>;
using Packs = arg::Placeholders<PackP, PackR, PackX>;
Task task;
Select select;
std::size_t n;
};
template<>
struct SkeletonTraits<IterSel> {
static constexpr bool serial = true;
template<typename Skeleton, typename F, typename T>
static constexpr decltype(auto) traverse(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init) {
using TraverserTask = SkeletonTraversal<typename std::decay_t<Skeleton>::Task>;
using TraverserSelect = SkeletonTraversal<typename std::decay_t<Skeleton>::Select>;
return function(parDepth, skeleton,
TraverserTask::execute(parDepth, skeleton.task, function, std::forward<T>(init)),
TraverserSelect::execute(parDepth, skeleton.select, function, std::forward<T>(init))
);
}
template<typename Skeleton>
static constexpr std::size_t parallelizability(Skeleton&&) noexcept {
return 1;
}
};
template<typename... Args, typename F>
void skeletonTraversal(IterSel<Args...> const& s, F f) {
f(s.task, 1);
f(s.select, 1);
}
}
#endif

View File

@ -0,0 +1,51 @@
#ifndef ALSK_ALSK_SKELETON_BONE_LOOP_H
#define ALSK_ALSK_SKELETON_BONE_LOOP_H
#include "../bonebase.h"
namespace alsk {
template<typename, typename> struct Loop;
template<
typename Signature,
typename Task_, typename TaskLinks_
>
struct Loop<Signature, tmp::Pack<Task_, TaskLinks_>>: SkeletonBase {
using Task = Task_;
using TaskLinks = TaskLinks_;
using Links = tmp::Pack<TaskLinks>;
using PackP = tmp::Parameters<Signature>;
using PackR = Returns<Links>;
using PackX = void;
using Packs = arg::Placeholders<PackP, PackR, PackX>;
Task task;
std::size_t n;
};
template<>
struct SkeletonTraits<Loop> {
static constexpr bool serial = true;
template<typename Skeleton, typename F, typename T>
static constexpr decltype(auto) traverse(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init) {
using Traverser = SkeletonTraversal<typename std::decay_t<Skeleton>::Task>;
return function(parDepth, skeleton, Traverser::execute(parDepth, skeleton.task, function, std::forward<T>(init)));
}
template<typename Skeleton>
static constexpr std::size_t parallelizability(Skeleton&&) noexcept {
return 1;
}
};
template<typename... Args, typename F>
void skeletonTraversal(Loop<Args...> const& s, F&& f) {
std::forward<F>(f)(s.task, 1);
}
}
#endif

View File

@ -0,0 +1,70 @@
#ifndef ALSK_ALSK_SKELETON_BONE_SERIAL_H
#define ALSK_ALSK_SKELETON_BONE_SERIAL_H
#include "../bonebase.h"
namespace alsk {
template<typename, typename...> struct Serial;
template<typename Signature, typename... FunTypes, typename... LinksTypes>
struct Serial<Signature, tmp::Pack<FunTypes, LinksTypes>...>: SkeletonBase {
using Links = tmp::Pack<LinksTypes...>;
using PackP = tmp::Parameters<Signature>;
using PackR = Returns<Links>;
using PackX = tmp::Pack<>;
using Packs = arg::Placeholders<PackP, PackR, PackX>;
using TasksP = tmp::Pack<FunTypes...>;
using Tasks = std::tuple<FunTypes...>;
Tasks tasks;
template<std::size_t I>
constexpr auto const& task() const noexcept { return std::get<I>(tasks); }
template<std::size_t I>
constexpr auto& task() noexcept { return std::get<I>(tasks); }
};
template<>
struct SkeletonTraits<Serial> {
static constexpr bool serial = true;
template<typename Skeleton, typename F, typename T>
static constexpr decltype(auto) traverse(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init) {
auto indices = std::make_index_sequence<std::tuple_size<decltype(skeleton.tasks)>{}>();
return _traverse(parDepth, std::forward<Skeleton>(skeleton), std::forward<F>(function), std::forward<T>(init), std::move(indices));
}
template<typename Skeleton, typename F, typename T, std::size_t... Is>
static constexpr decltype(auto) _traverse(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init, std::index_sequence<Is...>) {
using Traversers = tmp::Pack<
SkeletonTraversal<std::tuple_element_t<Is, typename std::decay_t<Skeleton>::Tasks>>...
>;
return function(parDepth, skeleton, tmp::PackGet<Traversers, Is>::execute(parDepth, skeleton.template task<Is>(), function, init)...);
}
template<typename Skeleton>
static constexpr std::size_t parallelizability(Skeleton&&) noexcept {
return 1;
}
};
namespace impl {
template<typename Tuple, typename F, std::size_t... indices>
void skeletonTraversalSerial(Tuple const& tasks, F f, std::index_sequence<indices...>) {
using Expander = int[];
static_cast<void>(Expander{(f(std::get<indices>(tasks), 1), 0)...});
}
}
template<typename Signature, typename... Tasks, typename F>
void skeletonTraversal(Serial<Signature, Tasks...> const& s, F&& f) {
impl::skeletonTraversalSerial(s.tasks, std::forward<F>(f), std::make_index_sequence<sizeof...(Tasks)>());
}
}
#endif

View File

@ -0,0 +1,63 @@
#ifndef ALSK_ALSK_SKELETON_BONE_WHILE_H
#define ALSK_ALSK_SKELETON_BONE_WHILE_H
#include "../bonebase.h"
namespace alsk {
template<typename, typename, typename> struct While;
template<
typename Signature,
typename Test_, typename TestLinks_,
typename Task_, typename TaskLinks_
>
struct While<Signature, tmp::Pack<Test_, TestLinks_>, tmp::Pack<Task_, TaskLinks_>>: SkeletonBase {
using Test = Test_;
using TestLinks = TestLinks_;
using Task = Task_;
using TaskLinks = TaskLinks_;
using Links = tmp::Pack<TestLinks_, TaskLinks_>;
using PackP = tmp::Parameters<Signature>;
using PackR = Returns<Links>;
using PackX = tmp::Pack<>;
using Packs = arg::Placeholders<PackP, PackR, PackX>;
Test test;
Task task;
~While() noexcept {}; // -Winline
};
template<>
struct SkeletonTraits<While> {
static constexpr bool serial = true;
template<typename Skeleton, typename F, typename T>
static constexpr decltype(auto) traverse(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init) {
using TraverserTest = SkeletonTraversal<typename std::decay_t<Skeleton>::Test>;
using TraverserTask = SkeletonTraversal<typename std::decay_t<Skeleton>::Task>;
return function(parDepth,
skeleton,
TraverserTest::execute(parDepth, skeleton.task, function, std::forward<T>(init)),
TraverserTask::execute(parDepth, skeleton.task, function, std::forward<T>(init))
);
}
template<typename Skeleton>
static constexpr std::size_t parallelizability(Skeleton&&) noexcept {
return 1;
}
};
template<typename... Args, typename F>
void skeletonTraversal(While<Args...> const& s, F f) {
f(s.test, 1);
f(s.task, 1);
}
}
#endif

View File

@ -0,0 +1,9 @@
#ifndef ALSK_ALSK_SKELETON_BONEBASE_H
#define ALSK_ALSK_SKELETON_BONEBASE_H
#include "skeletonbase.h"
#include "traits.h"
#include "link/args.h"
#endif

11
inc/alsk/skeleton/bones.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef ALSK_ALSK_SKELETON_BONE_H
#define ALSK_ALSK_SKELETON_BONE_H
#include "bone/farm.h"
#include "bone/farmsel.h"
#include "bone/itersel.h"
#include "bone/loop.h"
#include "bone/serial.h"
#include "bone/while.h"
#endif

View File

@ -0,0 +1,7 @@
#ifndef ALSK_ALSK_SKELETON_LINK_ARGS_H
#define ALSK_ALSK_SKELETON_LINK_ARGS_H
#include "args/placeholders.h"
#include "args/traits.h"
#endif

View File

@ -0,0 +1,32 @@
#ifndef ALSK_ALSK_SKELETON_LINK_ARGS_PLACEHOLDERS_H
#define ALSK_ALSK_SKELETON_LINK_ARGS_PLACEHOLDERS_H
#include <cstdint>
#include <tmp/pack.h>
namespace alsk {
namespace arg {
/**
* @brief argument placeholder for parameters
*/
template<std::size_t I>
struct P: std::integral_constant<std::size_t, I> {};
/**
* @brief argument placeholder for returned values
*/
template<std::size_t I>
struct R: std::integral_constant<std::size_t, I> {};
/**
* @brief argument placeholder for contextual arguments
*/
template<std::size_t I>
struct C: std::integral_constant<std::size_t, I> {};
}
}
#endif

View File

@ -0,0 +1,226 @@
#ifndef ALSK_ALSK_SKELETON_LINK_ARGS_TRAITS_H
#define ALSK_ALSK_SKELETON_LINK_ARGS_TRAITS_H
#include <functional>
#include <type_traits>
#include <utility>
#include <tmp/pack.h>
#include <tmp/traits.h>
#include "placeholders.h"
namespace alsk {
namespace arg {
/**
* @brief true if the given type is a placeholder for a parameter
*/
template<typename T> struct IsP: std::false_type {};
template<std::size_t I> struct IsP<P<I>>: std::true_type {};
template<typename T>
constexpr bool isP = IsP<T>::value;
/**
* @brief true if the given type is a placeholder for a returned value
*/
template<typename T> struct IsR: std::false_type {};
template<std::size_t I> struct IsR<R<I>>: std::true_type {};
template<typename T>
constexpr bool isR = IsR<T>::value;
/**
* @brief true if the given type is a placeholder for a contextual argument
*/
template<typename T> struct IsC: std::false_type {};
template<std::size_t I> struct IsC<C<I>>: std::true_type {};
template<typename T>
constexpr bool isC = IsC<T>::value;
/**
* @brief placeholders group
*/
template<typename P_, typename R_, typename C_>
struct Placeholders {
using P = tmp::AsPack<std::decay_t<P_>>;
using R = tmp::AsPack<std::decay_t<R_>>;
using C = tmp::AsPack<std::decay_t<C_>>;
};
}
/**
* @brief apply type mods from A to B
*/
template<typename, typename B>
struct ApplyTypeModsImpl {
using type = B; // TODO: std::decay_t<B>
};
template<typename A, typename B>
struct ApplyTypeModsImpl<A const&, B> {
using type = std::decay_t<B> const&;
};
template<typename A, typename B>
using ApplyTypeMods = typename ApplyTypeModsImpl<A, B>::type;
/**
* @brief ArgType
*/
template<typename T, typename, typename=void>
struct ArgTypeImpl {
using type = T;
};
template<typename A, typename Placeholders>
struct ArgTypeImpl<A, Placeholders, std::enable_if_t<arg::isP<std::decay_t<A>>>> {
using type = tmp::PackGet<typename Placeholders::P, std::decay_t<A>::value>;
};
template<typename A, typename Placeholders>
struct ArgTypeImpl<A, Placeholders, std::enable_if_t<arg::isR<std::decay_t<A>>>> {
using type = tmp::PackGet<typename Placeholders::R, std::decay_t<A>::value>;
};
template<typename A, typename Placeholders>
struct ArgTypeImpl<A, Placeholders, std::enable_if_t<arg::isC<std::decay_t<A>>>> {
using type = tmp::PackGet<typename Placeholders::C, std::decay_t<A>::value>;
};
template<typename T, typename Placeholders>
using ArgType = typename ArgTypeImpl<T, Placeholders>::type;
/**
* @brief ArgGet
*/
template<typename, typename=void> struct ArgGet;
template<>
struct ArgGet<void> {
template<typename P, typename R, typename C>
static void get(P&&, R&&, C&&) {}
};
template<typename A>
struct ArgGet<A, std::enable_if_t<arg::isP<std::decay_t<A>>>> {
template<typename P, typename R, typename C>
static auto get(P&& p, R&&, C&&)
-> /*ApplyTypeMods<A,*/ // TODO: does not work
ArgType<A, arg::Placeholders<P, tmp::Pack<>, tmp::Pack<>>>
/*>*/ {
return std::get<std::decay_t<A>::value>(std::forward<P>(p));
}
};
template<typename A>
struct ArgGet<A, std::enable_if_t<arg::isR<std::decay_t<A>>>> {
template<typename P, typename R, typename C>
static auto get(P&&, R&& r, C&&)
-> ApplyTypeMods<A,
ArgType<A, arg::Placeholders<tmp::Pack<>, R, tmp::Pack<>>>
> {
return std::get<std::decay_t<A>::value>(std::forward<R>(r));
}
};
template<typename A>
struct ArgGet<A, std::enable_if_t<arg::isC<std::decay_t<A>>>> {
template<typename P, typename R, typename C>
static auto get(P&&, R&&, C&& x)
-> ApplyTypeMods<A,
ArgType<A, arg::Placeholders<tmp::Pack<>, tmp::Pack<>, C>>
> {
return std::get<std::decay_t<A>::value>(std::forward<C>(x));
}
};
/**
* @brief IsPlaceholder
*/
namespace impl {
template<typename A>
struct IsPlaceholder:
std::integral_constant<bool,
arg::isP<std::decay_t<A>> or arg::isR<std::decay_t<A>> or arg::isC<std::decay_t<A>>
> {};
template<typename A>
constexpr bool isPlaceholder = IsPlaceholder<A>::value;
}
/**
* @brief ArgFilter
*/
template<typename, typename = void> struct ArgFilterImpl;
template<typename Ret, typename T, typename... Ts>
struct ArgFilterImpl<Ret(T, Ts...), std::enable_if_t<impl::isPlaceholder<T>>> {
using type = tmp::FunctionCat<Ret(T), typename ArgFilterImpl<Ret(Ts...)>::type>;
};
template<typename Ret, typename T, typename... Ts>
struct ArgFilterImpl<Ret(T, Ts...), std::enable_if_t<!impl::isPlaceholder<T>>> {
using type = typename ArgFilterImpl<Ret(Ts...)>::type;
};
template<typename Ret>
struct ArgFilterImpl<Ret()> {
using type = Ret();
};
template<typename F>
using ArgFilter = typename ArgFilterImpl<F>::type;
/**
* @brief RealTypeImpl
* @param T a type to solve
* @param Placeholders the placeholders list
*/
template<typename T, typename Placeholders>
struct RealTypeImpl {
using type = ArgType<T, Placeholders>;
};
/**
* @brief RealType
* @param T a type to solve
* @param Placeholders the placeholders list
*/
template<typename T, typename Placeholders>
using RealType = typename RealTypeImpl<T, Placeholders>::type;
/**
* @brief RealSignature
*/
template<typename, typename> struct RealSignatureImpl;
template<typename Ret, typename... Ps, typename Placeholders>
struct RealSignatureImpl<Ret(Ps...), Placeholders> {
// using type = Ret(*)(typename RealType<Ps, P, R>::type...); // issue with const& parameters
using type = std::function<Ret(RealType<Ps, Placeholders>...)>;
};
template<typename T, typename Placeholders>
using RealSignature = typename RealSignatureImpl<T, Placeholders>::type;
/**
* @brief Returns
*/
template<typename> struct ReturnsImpl;
template<typename... Fs>
struct ReturnsImpl<tmp::Pack<Fs...>> {
using type = tmp::Pack<tmp::ReturnType<Fs>...>;
};
template<typename T>
using Returns = typename ReturnsImpl<T>::type;
}
#endif

View File

@ -0,0 +1,52 @@
#ifndef ALSK_ALSK_SKELETON_LINK_LINK_H
#define ALSK_ALSK_SKELETON_LINK_LINK_H
#include <tmp/pack.h>
#include "args.h"
#include "utility.h"
#include "../struct/struct.h"
namespace alsk {
/**
* @brief hold the skeleton links
*/
template<template<typename...> class T, typename F, typename... Ts>
using L = S<T, F, Ts...>;
namespace impl {
/**
* Pack of return types from links
*/
template<typename...> struct LinksToReturnTypes;
template<typename R, typename... Params, typename... Ls>
struct LinksToReturnTypes<R(Params...), Ls...> {
using type = tmp::PackCat<tmp::Pack<R>, typename LinksToReturnTypes<Ls...>::type>;
};
template<template<typename...> class Skel, typename R, typename... Params, typename... InnerLs, typename... Ls>
struct LinksToReturnTypes<alsk::L<Skel, R(Params...), InnerLs...>, Ls...> {
using type = tmp::PackCat<tmp::Pack<R>, typename LinksToReturnTypes<Ls...>::type>;
};
template<template<typename...> class Skel, std::size_t I, typename... Params, typename... InnerLs, typename... Ls>
struct LinksToReturnTypes<alsk::L<Skel, alsk::arg::R<I>(Params...), InnerLs...>, Ls...> {
using rets = typename LinksToReturnTypes<InnerLs...>::type;
using R = tmp::PackGet<rets, I>;
using type = tmp::PackCat<tmp::Pack<R>, typename LinksToReturnTypes<Ls...>::type>;
};
template<>
struct LinksToReturnTypes<> {
using type = tmp::Pack<>;
};
}
}
#endif

View File

@ -0,0 +1,27 @@
#ifndef ALSK_ALSK_SKELETON_LINK_UTILITY_H
#define ALSK_ALSK_SKELETON_LINK_UTILITY_H
#include "args.h"
namespace alsk {
template<typename> struct AutoLinkSerialImpl;
template<typename R, typename... Args>
struct AutoLinkSerialImpl<R(Args...)> {
template<typename> struct Helper;
template<std::size_t... indices>
struct Helper<std::index_sequence<indices...>> {
using in = R(alsk::arg::P<indices>...);
using out = alsk::arg::R<0>(Args...);
};
using type = Helper<decltype(std::make_index_sequence<sizeof...(Args)>())>;
};
template<typename Signature>
using AutoLinkSerial = typename AutoLinkSerialImpl<Signature>::type;
}
#endif

View File

@ -0,0 +1,50 @@
#ifndef ALSK_ALSK_SKELETON_MUSCLE_MUSCLE_H
#define ALSK_ALSK_SKELETON_MUSCLE_MUSCLE_H
#include <tmp/pack.h>
namespace alsk {
/**
* @brief hold the functionoid type and its links
*/
template<typename Type, typename Links>
using Fun = tmp::Pack<Type, Links>;
}
template<typename T, T> struct Fn;
template<typename R, typename... Ts, R(*F)(Ts...)>
struct Fn<R(*)(Ts...), F> {
using Signature = R(Ts...);
constexpr R operator()(Ts... args) { return F(args...); }
};
template<typename R, typename... Ts, R(&F)(Ts...)>
struct Fn<R(&)(Ts...), F> {
using Signature = R(Ts...);
constexpr R operator()(Ts... args) { return F(args...); }
};
#define FN(f) Fn<decltype(f)&, f>
#define FN_OVERLOAD(T, f) Fn<T, f>
/** C++17
* namespace impl {
*
* template<typename T, T> struct Fn;
*
* template<typename R, typename... Ts, R(*F)(Ts...)>
* struct Fn<R(*)(Ts...), F> {
* R operator()(Ts... args) { return F(args...); }
* };
*
* }
*
* template<auto F>
* using Fn = impl::Fn<decltype(F), F>;
*/
#endif

View File

@ -0,0 +1,11 @@
#ifndef ALSK_ALSK_SKELETON_SKELETON_H
#define ALSK_ALSK_SKELETON_SKELETON_H
#include "bones.h"
#include "utility.h"
#include "link/link.h"
#include "muscle/muscle.h"
#include "struct/struct.h"
#endif

View File

@ -0,0 +1,23 @@
#ifndef ALSK_ALSK_SKELETON_SKELETONBASE_H
#define ALSK_ALSK_SKELETON_SKELETONBASE_H
#include <cstdint>
#include "tags.h"
namespace alsk {
struct SkeletonBase {
using IsSkeleton = tag::SkeletonTag;
constexpr SkeletonBase() noexcept {}
~SkeletonBase() noexcept {}
std::size_t step = 0;
std::size_t parDepth = 0;
};
}
#endif

View File

@ -0,0 +1,17 @@
#ifndef ALSK_ALSK_SKELETON_STRUCT_STRUCT_H
#define ALSK_ALSK_SKELETON_STRUCT_STRUCT_H
namespace alsk {
/**
* @brief hold the skeleton structure
*/
template<template<typename...> class T, typename... Ts>
struct S {
template<typename... Params>
using type = T<Params...>;
};
}
#endif

12
inc/alsk/skeleton/tags.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef ALSK_ALSK_SKELETON_TAGS_H
#define ALSK_ALSK_SKELETON_TAGS_H
namespace alsk {
namespace tag {
struct SkeletonTag {};
}
}
#endif

View File

@ -0,0 +1,81 @@
#ifndef ALSK_ALSK_SKELETON_TRAITS_H
#define ALSK_ALSK_SKELETON_TRAITS_H
#include <type_traits>
#include "tags.h"
namespace alsk {
/**
* @brief true if the given type is a Skeleton
*/
template<typename T, typename = void>
struct IsSkeleton: std::false_type {};
template<typename T>
struct IsSkeleton<T, std::enable_if_t<std::is_same<typename std::decay_t<T>::IsSkeleton, tag::SkeletonTag>{}>>: std::true_type {};
template<typename T>
constexpr bool isSkeleton = IsSkeleton<T>::value;
/**
* @brief class to determine a skeleton traits
* @param serial true if the skeleton cannot be run in parallel
*/
template<template<typename...> class S>
struct SkeletonTraits {
static constexpr bool serial = true;
};
template<typename> struct SkeletonTraitsTImpl;
template<template<typename...> class S, typename... Ts>
struct SkeletonTraitsTImpl<S<Ts...>> {
using type = SkeletonTraits<S>;
};
template<typename S>
using SkeletonTraitsT = typename SkeletonTraitsTImpl<std::decay_t<S>>::type;
/**
* @brief function to dynamically traverse a skeleton tree
*
* by default, do nothing (typically muscle case)
*/
template<typename Muscle, typename F,
std::enable_if_t<not isSkeleton<std::decay_t<Muscle>>>* = nullptr
>
void skeletonTraversal(Muscle&&, F&&) {}
/**
* @brief template to specialize to implement a bone
*/
template<typename Skeleton, typename Tag, typename Executor, typename Context> struct Impl;
/**
* @brief template to specialize to define how to traverse a bone
*/
template<typename Skeleton, typename=void>
struct SkeletonTraversal;
/**
* @brief returns steps between two instances of the skeleton
*/
template<typename Skeleton, std::enable_if_t<isSkeleton<Skeleton>>* = nullptr>
constexpr std::size_t skeletonStep(Skeleton const& s) {
return s.step;
}
/**
* @brief returns 0
*/
template<typename Skeleton, std::enable_if_t<not isSkeleton<Skeleton>>* = nullptr>
constexpr std::size_t skeletonStep(Skeleton const&) {
return 0;
}
}
#endif

247
inc/alsk/skeleton/utility.h Normal file
View File

@ -0,0 +1,247 @@
#ifndef ALSK_ALSK_SKELETON_UTILITY_H
#define ALSK_ALSK_SKELETON_UTILITY_H
#include <tmp/algorithm.h>
#include <tmp/tree.h>
#include "link/link.h"
#include "struct/struct.h"
#include "muscle/muscle.h"
#include "traits.h"
namespace alsk {
namespace impl {
/**
* Build inner skeletons
*/
template<typename, typename, typename, typename, typename> struct BuildSkeletonParts;
template<
template<typename...> class Skel, typename... Ss,
typename R, typename... Params, typename... Ls,
typename PParams, typename RParams, typename XParams
>
struct BuildSkeletonParts<alsk::S<Skel, Ss...>, alsk::L<Skel, R(Params...), Ls...>, PParams, RParams, XParams> {
using Placeholders = arg::Placeholders<PParams, RParams, XParams>;
using prms = tmp::Pack<RealType<Params, Placeholders>...>;
using rets = typename LinksToReturnTypes<Ls...>::type;
using xprm = tmp::Pack<>;
using type = alsk::Fun<
Skel<
R(RealType<Params, Placeholders>...),
typename BuildSkeletonParts<Ss, Ls, prms, rets, xprm>::type...
>,
RealType<R, arg::Placeholders<tmp::Pack<>, rets, xprm>>(Params...)
>;
};
template<typename S, typename L, typename, typename, typename>
struct BuildSkeletonParts {
using type = alsk::Fun<S, L>;
};
/**
* Build outer skeleton
*/
template<typename, typename> struct BuildSkeleton;
template<template<typename...> class Skel, typename... Ss, typename R, typename... Params, typename... Ls>
struct BuildSkeleton<alsk::S<Skel, Ss...>, alsk::L<Skel, R(Params...), Ls...>> {
using rets = typename LinksToReturnTypes<Ls...>::type;
using xprm = tmp::Pack<>;
using type = Skel<
R(Params...),
typename BuildSkeletonParts<Ss, Ls, tmp::Pack<Params...>, rets, xprm>::type...
>;
};
}
/**
* Build a skeleton from its structure and its links
*/
template<typename S, typename L>
using BuildSkeletonT = typename impl::BuildSkeleton<S, L>::type;
template<template<typename...> class S, template<typename...> class L>
struct BuildSkeleton {
template<typename, typename> struct Type;
template<typename... SParams, typename... LParams>
struct Type<tmp::Pack<SParams...>, tmp::Pack<LParams...>> {
using type = BuildSkeletonT<S<SParams...>, L<LParams...>>;
};
template<typename SParams, typename LParams>
using type = typename Type<SParams, LParams>::type;
template<typename SParams, typename LParams>
using skeleton = typename Type<SParams, LParams>::type;
};
// is it possible?
// template<template<typename...> class S, template<typename...> class L>
// template<typename SParams, typename LParams>
// using type = typename BuildSkeleton<S, L>::type<SParams, LParams>;
template<template<typename...> class Skeleton, typename S, typename L = S>
struct Branch {
template<typename... Ts>
using skeleton = Skeleton<Ts...>;
using signature = S;
using links = L;
};
template<typename T, typename L>
struct Leaf {
using type = T;
using links = L;
};
/**
* TreeFromSkeleton
*/
template<typename, typename=void> struct TreeFromSkeletonImpl;
template<template<typename...> class Skeleton, typename S, typename... Ps>
struct TreeFromSkeletonImpl<Skeleton<S, Ps...>, std::enable_if_t<isSkeleton<Skeleton<S, Ps...>>>> {
using type = tmp::Tree<Branch<Skeleton, S, S>, typename TreeFromSkeletonImpl<Ps>::type...>;
};
template<template<typename...> class Skeleton, typename S, typename... Ps, typename L>
struct TreeFromSkeletonImpl<Fun<Skeleton<S, Ps...>, L>, std::enable_if_t<isSkeleton<Skeleton<S, Ps...>>>> {
using type = tmp::Tree<Branch<Skeleton, S, L>, typename TreeFromSkeletonImpl<Ps>::type...>;
};
template<typename T, typename L>
struct TreeFromSkeletonImpl<Fun<T, L>, std::enable_if_t<not isSkeleton<T>>> {
using type = tmp::Tree<Leaf<T, L>>;
};
template<typename T>
using TreeFromSkeleton = typename TreeFromSkeletonImpl<T>::type;
/**
* SkeletonFromTree
*/
template<typename> struct SkeletonFromTreeImpl;
template<template<typename...> class Skeleton, typename S, typename L, typename... Cs>
struct SkeletonFromTreeImpl<tmp::Tree<Branch<Skeleton, S, L>, Cs...>> {
using type = Skeleton<S, typename SkeletonFromTreeImpl<Cs>::fun...>;
using fun = Fun<type, L>;
};
template<typename T, typename L, typename... Cs>
struct SkeletonFromTreeImpl<tmp::Tree<Leaf<T, L>, Cs...>> {
using type = T;
using fun = Fun<type, L>;
};
template<typename T>
using SkeletonFromTree = typename SkeletonFromTreeImpl<T>::type;
/**
*
*/
template<typename> struct GetTemplate;
template<template<typename...> class TT, typename... Ps>
struct GetTemplate<TT<Ps...>> {
template<typename... As>
using tmpl = TT<As...>;
};
template<template<typename...> class Skel, typename... Skargs>
struct SkeletonTraversal<Skel<Skargs...>, std::enable_if_t<isSkeleton<Skel<Skargs...>>>> {
template<typename Skeleton, typename F, typename T>
static decltype(auto) execute(std::size_t parDepth, Skeleton&& skeleton, F&& function, T&& init) {
using Traits = SkeletonTraits<Skel>;
return Traits::traverse(parDepth, std::forward<Skeleton>(skeleton), std::forward<F>(function), std::forward<T>(init));
}
template<typename Skeleton, typename F, typename T>
static decltype(auto) execute(Skeleton&& skeleton, F&& function, T&& init) {
using Traits = SkeletonTraits<Skel>;
return Traits::traverse(0, std::forward<Skeleton>(skeleton), std::forward<F>(function), std::forward<T>(init));
}
};
template<typename S>
struct SkeletonTraversal<S, std::enable_if_t<not isSkeleton<S>>> {
template<typename Skeleton, typename F, typename T>
static T execute(std::size_t, Skeleton&&, F&&, T&& init) { return init; }
template<typename Skeleton, typename F, typename T>
static T execute(Skeleton&&, F&&, T&& init) { return init; }
};
}
namespace alsk {
/*
template<typename S>
struct SkeletonParallelHeightImpl {
template<typename> struct CheckParallelityImpl;
template<template<typename...> class Skel, typename Sign, typename Link>
struct CheckParallelityImpl<Branch<Skel, Sign, Link>> {
using type = std::integral_constant<bool, not SkeletonTraits<Skel>::serial>;
};
template<typename T, typename Link>
struct CheckParallelityImpl<Leaf<T, Link>> {
using type = std::integral_constant<bool, false>;
};
template<typename P>
using CheckParallelity = typename CheckParallelityImpl<P>::type;
template<typename P>
using CheckParallelityEach = tmp::Transform<P, CheckParallelity>;
template<typename LHS, typename RHS>
using Sum = std::integral_constant<decltype(LHS::value), LHS::value + RHS::value>;
template<typename P>
using SumEach = tmp::Accumulate<P, Sum, std::integral_constant<tmp::Depth, 0>>;
template<typename LHS, typename RHS>
using Max = std::integral_constant<decltype(LHS::value), (LHS::value>RHS::value)? LHS::value:RHS::value>;
using tree = TreeFromSkeleton<S>;
using rtlpaths = tmp::TreeAllRTLPaths<tree>;
using parallel = tmp::Transform<rtlpaths, CheckParallelityEach>;
using parallelc = tmp::Transform<parallel, SumEach>;
using result = tmp::Accumulate<parallelc, Max, std::integral_constant<tmp::Depth, 0>>;
static constexpr tmp::Depth value = result::value;
};
/*/
template<typename S>
struct SkeletonParallelHeightImpl {
template<typename, typename...> struct CalcHeightImpl;
template<template<typename...> class Skel, typename Sign, typename Link, typename... Ts>
struct CalcHeightImpl<Branch<Skel, Sign, Link>, Ts...> {
static constexpr tmp::Depth par = SkeletonTraits<Skel>::serial? 0:1;
using type = std::integral_constant<tmp::Depth, par + tmp::detail::Max<Ts::type::value...>>;
};
template<typename T, typename Link, typename... Ts>
struct CalcHeightImpl<Leaf<T, Link>, Ts...> {
using type = std::integral_constant<tmp::Depth, 0>;
};
template<typename P, typename... Ts>
using CalcHeight = typename CalcHeightImpl<P, Ts...>::type;
using CalcHeightDefault = std::integral_constant<tmp::Depth, 0>;
using tree = TreeFromSkeleton<S>;
static constexpr tmp::Depth value = tmp::TreeAccumulate<tree, CalcHeight, CalcHeightDefault>::value;
};
//*/
template<typename S>
constexpr tmp::Depth skeletonParallelHeight = SkeletonParallelHeightImpl<S>::value;
}
#endif

97
inc/alsk/utility.h Normal file
View File

@ -0,0 +1,97 @@
#ifndef ALSK_ALSK_UTILITY_H
#define ALSK_ALSK_UTILITY_H
#include <functional>
#include <string>
#include <tmp/traits.h>
#include <tmp/pack.h>
namespace alsk {
/**
* @brief Tuple printer of ::size()
*/
namespace impl {
template<std::size_t I, typename T, typename = void> struct TupleElementImpl;
template<std::size_t I, typename... Ts>
struct TupleElementImpl<I, std::tuple<Ts...>, std::enable_if_t<(I < sizeof...(Ts))>> {
using type = typename std::tuple_element<I, std::tuple<Ts...>>::type;
};
template<std::size_t I, typename... Ts>
struct TupleElementImpl<I, std::tuple<Ts...>, std::enable_if_t<(I >= sizeof...(Ts))>> {
using type = void;
};
template<std::size_t I, typename T>
using TupleElement = typename TupleElementImpl<I, T>::type;
template<typename T, typename = void> struct HasSize: std::false_type {};
template<typename T> struct HasSize<T, tmp::void_t<decltype(std::declval<std::decay_t<T>>().size())>>: std::true_type {};
template<std::size_t I, std::size_t N, typename T, typename = void> struct PTuple;
template<std::size_t I, std::size_t N, typename T>
struct PTuple<I, N, T, std::enable_if_t<I != N && not HasSize<TupleElement<I, std::decay_t<T>>>{}>> {
static void print(std::string const& lbl, T const& t) {
PTuple<I+1, N, T>::print(lbl, t);
}
};
template<std::size_t I, std::size_t N, typename T>
struct PTuple<I, N, T, tmp::void_t<std::enable_if_t<I != N && HasSize<TupleElement<I, std::decay_t<T>>>{}>>> {
static void print(std::string const& lbl, T const& t) {
std::printf("%s: %zu\n", lbl.c_str(), std::get<I>(t).size());
PTuple<I+1, N, T>::print(lbl, t);
}
};
template<std::size_t I, typename T>
struct PTuple<I, I, T> {
static void print(std::string const&, T const&) {}
};
}
template<typename... Ts>
void pTuple(std::string const& lbl, std::tuple<Ts...> const& t) {
impl::PTuple<0, sizeof...(Ts), std::tuple<Ts...>>::print(lbl, t);
}
/* TODO: find a place */
struct Less {
template<typename T>
bool operator()(T const& lhs, T const& rhs) const { return lhs < rhs; }
};
template<typename T>
static auto max(T&& value) {
return std::forward<T>(value);
}
template<typename T, typename... Ts, std::enable_if_t<sizeof...(Ts) != 0>* = nullptr>
static constexpr auto max(T const& value, Ts&&... values) noexcept {
auto m = max(std::forward<Ts>(values)...);
return std::max(m, value);
}
/**
* helper to write recursive lambda functions
*/
template<typename F>
struct RecursiveLambda {
F f;
template<typename... Args>
void operator()(Args&&... args) const {
f(std::ref(*this), std::forward<Args>(args)...);
}
};
template<typename F>
static RecursiveLambda<std::decay_t<F>> makeRecursiveLambda(F&& f) {
return {std::forward<F>(f)};
}
}
#endif