#pragma once #include #include #include #include #include #include // HINT: c++17 does not support std::is_constant_evaluated() yet, so we use __builtin_is_constant_evaluated() __implemented by // each compiler instead. and to be said, __builtin_is_constant_evaluated() is tested to be valid under c++17 with gcc, clang // and msvc. // HINT: for algorithm listed below, we use oneDPL library to accelerate it when it is executed under runtime. // Othereise, we will use s__imple loop and local variables to __implement it, since these features are guaranteed to be valid // since c++14, but some algorithms may not be to evaluated as constant expressions under c++17. // MODIFY: we do not support version function for now, since it is useless in our project namespace detail { using execution_policy_variant_t = std::variant; template auto execution_policy_selector(_Tp N) -> execution_policy_variant_t { static_assert(!std::is_floating_point_v<_Tp>); if (N <= 16) if (N % 4 == 0) return dpl::execution::unseq; else return dpl::execution::seq; else if (N % 4 == 0) return dpl::execution::par_unseq; else return dpl::execution::par; } template auto execution_policy_selector_simd_only(_Tp N) -> execution_policy_variant_t { static_assert(!std::is_floating_point_v<_Tp>); if (N <= 16) return dpl::execution::unseq; else return dpl::execution::par_unseq; } static double thread_overload_factor = 1.5; template static inline auto decide_available_threads(_Tp max_works_expected) { static_assert(!std::is_floating_point_v<_Tp>); return std::min(static_cast(max_works_expected), static_cast(thread_overload_factor * std::thread::hardware_concurrency())); } } // namespace detail namespace algorithm { enum class ExecutionPolicySelector : bool { all, simd_only }; template inline void for_loop(_Tp first, _Tp last, UnaryFunc f) { static_assert(std::is_integral_v<_Tp>); detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(last - first); else policy = detail::execution_policy_selector_simd_only(last - first); std::visit( [&](auto&& exec) { dpl::for_each(exec, oneapi::dpl::counting_iterator<_Tp>(first), oneapi::dpl::counting_iterator<_Tp>(last), f); }, policy); } template inline void for_each(InputIt first, InputIt last, UnaryFunc f) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); std::visit([&](auto&& exec) { dpl::for_each(exec, first, last, f); }, policy); } template inline ForwardIt min_element(ForwardIt first, ForwardIt last) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::min_element(exec, first, last); }, policy); } template inline ForwardIt max_element(ForwardIt first, ForwardIt last) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::max_element(exec, first, last); }, policy); } template inline void fill(ForwardIt first, ForwardIt last, const typename std::iterator_traits::value_type& value) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); std::visit([&](auto&& exec) { dpl::fill(exec, first, last, value); }, policy); } template inline OutputIt fill_n(OutputIt first, std::size_t count, const typename std::iterator_traits::value_type& value) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(count); else policy = detail::execution_policy_selector_simd_only(count); return std::visit([&](auto&& exec) { return dpl::fill_n(exec, first, count, value); }, policy); } template inline OutputIt copy(InputIt first, InputIt last, OutputIt d_first) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::copy(exec, first, last, d_first); }, policy); } template inline ForwardIt2 swap_ranges(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first1, last1)); else policy = detail::execution_policy_selector_simd_only(std::distance(first1, last1)); return std::visit([&](auto&& exec) { return dpl::swap_ranges(exec, first1, last1, first2); }, policy); } template inline typename std::iterator_traits::difference_type count(InputIt first, InputIt last, const typename std::iterator_traits::value_type& value) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::count(exec, first, last, value); }, policy); } template inline void replace(InputIt first, InputIt last, const typename std::iterator_traits::value_type& old_value, const typename std::iterator_traits::value_type& new_value) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); std::visit([&](auto&& exec) { dpl::replace(exec, first, last, old_value, new_value); }, policy); } template inline InputIt find(InputIt first, InputIt last, const typename std::iterator_traits::value_type& value) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::find(exec, first, last, value); }, policy); } template inline InputIt find_if(InputIt first, InputIt last, UnaryPred p) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::find_if(exec, first, last, p); }, policy); } template inline bool all_of(InputIt first, InputIt last, UnaryPred pred) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::all_of(exec, first, last, pred); }, policy); } template inline bool any_of(InputIt first, InputIt last, UnaryPred pred) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::any_of(exec, first, last, pred); }, policy); } template inline bool none_of(InputIt first, InputIt last, UnaryPred pred) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::none_of(exec, first, last, pred); }, policy); } template inline T reduce(InputIt first, InputIt last, T init, BinaryOp op) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::reduce(exec, first, last, init, op); }, policy); } template inline OutputIt transform(InputIt first, InputIt last, OutputIt d_first, UnaryOp unary_op) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::transform(exec, first, last, d_first, unary_op); }, policy); } template inline OutputIt transform(InputIt1 first1, InputIt1 last1, InputIt2 first2, OutputIt d_first, BinaryOp binary_op) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first1, last1)); else policy = detail::execution_policy_selector_simd_only(std::distance(first1, last1)); return std::visit([&](auto&& exec) { return dpl::transform(exec, first1, last1, first2, d_first, binary_op); }, policy); } template inline T transform_reduce(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init, BinaryOp1 reduce, BinaryOp2 transform) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first1, last1)); else policy = detail::execution_policy_selector_simd_only(std::distance(first1, last1)); return std::visit([&](auto&& exec) { return dpl::transform_reduce(exec, first1, last1, first2, init, reduce, transform); }, policy); } template inline T transform_reduce(InputIt1 first, InputIt1 last, T init, BinaryOp reduce, UnaryOp transform) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::transform_reduce(exec, first, last, init, reduce, transform); }, policy); } template inline OutputIt inclusive_scan(InputIt first, InputIt last, OutputIt d_first, T init, BinaryOp binary_op) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::inclusive_scan(exec, first, last, d_first, binary_op, init); }, policy); } template inline OutputIt exclusive_scan(InputIt first, InputIt last, OutputIt d_first, T init, BinaryOp binary_op) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::exclusive_scan(exec, first, last, d_first, init, binary_op); }, policy); } template inline ForwardIt shift_left(ForwardIt first, ForwardIt last, typename std::iterator_traits::difference_type n) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::shift_left(exec, first, last, n); }, policy); } template inline ForwardIt shift_right(ForwardIt first, ForwardIt last, typename std::iterator_traits::difference_type n) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::shift_right(exec, first, last, n); }, policy); } template ForwardIt rotate(ForwardIt first, ForwardIt middle, ForwardIt last) { detail::execution_policy_variant_t policy; if constexpr (Policy == ExecutionPolicySelector::all) policy = detail::execution_policy_selector(std::distance(first, last)); else policy = detail::execution_policy_selector_simd_only(std::distance(first, last)); return std::visit([&](auto&& exec) { return dpl::rotate(exec, first, middle, last); }, policy); } } // namespace algorithm