kfr

Fast, modern C++ DSP framework, FFT, Sample Rate Conversion, FIR/IIR/Biquad Filters (SSE, AVX, AVX-512, ARM NEON)
Log | Files | Refs | README

commit 2ccc349f8bf492174a944105cf26c793e46053b2
parent 41a39838e1195d1269c36bd9d88676e745f0300f
Author: d.levin256@gmail.com <d.levin256@gmail.com>
Date:   Sun, 21 Oct 2018 17:38:34 +0300

Optimize expression_vtable, add substitute

Diffstat:
Minclude/kfr/base/filter.hpp | 40++++++++++++++++++++++++++++++++++++++++
Minclude/kfr/base/pointer.hpp | 297++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
2 files changed, 214 insertions(+), 123 deletions(-)

diff --git a/include/kfr/base/filter.hpp b/include/kfr/base/filter.hpp @@ -26,6 +26,7 @@ #pragma once #include "basic_expressions.hpp" +#include "expression.hpp" #include "pointer.hpp" #include "univector.hpp" @@ -101,4 +102,43 @@ protected: virtual void process_buffer(T* dest, const T* src, size_t size) = 0; virtual void process_expression(T* dest, const expression_pointer<T>& src, size_t size) = 0; }; + +template <typename T> +class expression_filter : public filter<T> +{ +public: + template <typename... Args> + explicit expression_filter(expression_pointer<T>&& filter_expr) : filter_expr(std::move(filter_expr)) + { + } + +protected: + void process_buffer(T* dest, const T* src, size_t size) override + { + substitute(filter_expr, to_pointer(make_univector(src, size))); + process(make_univector(dest, size), filter_expr, 0, size); + } + void process_expression(T* dest, const expression_pointer<T>& src, size_t size) override + { + substitute(filter_expr, src); + process(make_univector(dest, size), filter_expr, 0, size); + } + + expression_pointer<T> filter_expr; +}; + +/// @brief Converts expression with placeholder to filter. Placeholder and filter must have the same type +template <typename E, typename T = value_type_of<E>> +KFR_SINTRIN expression_filter<T> to_filter(E&& e) +{ + return expression_filter<T>(to_pointer(std::move(e))); } + +/// @brief Converts expression with placeholder to filter. Placeholder and filter must have the same type +template <typename T, typename E> +KFR_SINTRIN expression_filter<T> to_filter(expression_pointer<T>&& e) +{ + return expression_filter<T>(std::move(e)); +} + +} // namespace kfr diff --git a/include/kfr/base/pointer.hpp b/include/kfr/base/pointer.hpp @@ -34,13 +34,79 @@ namespace kfr constexpr size_t maximum_expression_width = platform<float>::vector_capacity / 4; -constexpr size_t expression_vtable_size = 3 + ilog2(maximum_expression_width) + 1; +template <typename T, bool enable_resource = true> +struct expression_pointer; -template <typename T> -using expression_vtable = std::array<void*, expression_vtable_size>; +namespace internal +{ + +template <typename Expression, typename T, size_t key = 0> +KFR_SINTRIN bool invoke_substitute(Expression& expr, expression_pointer<T>&& new_pointer, + csize_t<key> = csize_t<key>{}); + +} + +template <typename T, size_t N = maximum_expression_width> +struct expression_vtable : expression_vtable<T, N / 2> +{ + using func_get = void (*)(void*, size_t, vec<T, N>&); + func_get get; + + template <typename Expression> + expression_vtable(ctype_t<Expression> t) + : expression_vtable<T, N / 2>(t), get(&expression_vtable<T, N>::template static_get<Expression>) + { + } + + template <typename Expression> + static void static_get(void* instance, size_t index, vec<T, N>& result) + { + result = static_cast<Expression*>(instance)->operator()(cinput, index, vec_t<T, N>()); + } +}; -struct dummy_content +template <typename T> +struct expression_vtable<T, 0> { + using func_size = size_t (*)(void* p); + using func_begin_block = void (*)(void*, size_t); + using func_end_block = void (*)(void*, size_t); + using func_substitute = bool (*)(void*, expression_pointer<T>&&); + + func_size size; + func_begin_block begin_block; + func_end_block end_block; + func_substitute substitute; + + template <typename Expression> + expression_vtable(ctype_t<Expression> t) + : size(&expression_vtable<T, 0>::template static_size<Expression>), + begin_block(&expression_vtable<T, 0>::template static_begin_block<Expression>), + end_block(&expression_vtable<T, 0>::template static_end_block<Expression>), + substitute(&expression_vtable<T, 0>::template static_substitute<Expression>) + { + } + + template <typename Expression> + static size_t static_size(void* instance) + { + return static_cast<Expression*>(instance)->size(); + } + template <typename Expression> + static void static_begin_block(void* instance, size_t size) + { + return static_cast<Expression*>(instance)->begin_block(cinput, size); + } + template <typename Expression> + static void static_end_block(void* instance, size_t size) + { + return static_cast<Expression*>(instance)->end_block(cinput, size); + } + template <typename Expression> + static bool static_substitute(void* instance, expression_pointer<T>&& ptr) + { + return internal::invoke_substitute(*static_cast<Expression*>(instance), std::move(ptr)); + } }; struct expression_resource @@ -48,25 +114,27 @@ struct expression_resource virtual ~expression_resource() {} virtual void* instance() { return nullptr; } }; + template <typename E> struct alignas(const_max(size_t(8), alignof(E))) expression_resource_impl : expression_resource { expression_resource_impl(E&& e) noexcept : e(std::move(e)) {} virtual ~expression_resource_impl() {} virtual void* instance() override final { return &e; } + private: E e; }; template <typename E> -std::shared_ptr<expression_resource> make_resource(E&& e) +KFR_SINTRIN std::shared_ptr<expression_resource> make_resource(E&& e) { using T = expression_resource_impl<decay<E>>; return std::static_pointer_cast<expression_resource>( std::allocate_shared<T>(allocator<T>(), std::move(e))); } -template <typename T, bool enable_resource = true> +template <typename T, bool enable_resource> struct expression_pointer : input_expression { using value_type = T; @@ -80,150 +148,45 @@ struct expression_pointer : input_expression template <size_t N, KFR_ENABLE_IF(N <= maximum_expression_width)> CMT_INLINE vec<T, N> operator()(cinput_t, size_t index, vec_t<T, N>) const { - using func_t = void (*)(void*, size_t, vec<T, N>&); - static_assert(is_poweroftwo(N), "N must be a power of two"); - constexpr size_t findex = ilog2(N); - func_t func = reinterpret_cast<func_t>((*vtable)[3 + findex]); vec<T, N> result; - func(instance, index, result); + static_cast<const expression_vtable<T, N>*>(vtable)->get(instance, index, result); return result; } template <size_t N, KFR_ENABLE_IF(N > maximum_expression_width)> CMT_INLINE vec<T, N> operator()(cinput_t cinput, size_t index, vec_t<T, N>) const { - return concat(operator()(cinput, index, vec_t<T, N / 2>()), - operator()(cinput, index + N / 2, vec_t<T, N / 2>())); - } - CMT_INLINE void begin_block(cinput_t, size_t size) const - { - using func_t = void (*)(void*, size_t); - func_t func = reinterpret_cast<func_t>((*vtable)[0]); - func(instance, size); - } - CMT_INLINE void end_block(cinput_t, size_t size) const - { - using func_t = void (*)(void*, size_t); - func_t func = reinterpret_cast<func_t>((*vtable)[1]); - func(instance, size); - } - CMT_INLINE size_t size() const - { - using func_t = size_t (*)(void*); - func_t func = reinterpret_cast<func_t>((*vtable)[2]); - return func(instance); + return concat(operator()(cinput, index, vec_t<T, N / 2>()), operator()(cinput, index + N / 2, + vec_t<T, N / 2>())); } + CMT_INLINE void begin_block(cinput_t, size_t size) const { vtable->begin_block(instance, size); } + CMT_INLINE void end_block(cinput_t, size_t size) const { vtable->end_block(instance, size); } + CMT_INLINE size_t size() const { return vtable->size(instance); } -private: - void* instance; - const expression_vtable<T>* vtable; - std::shared_ptr<expression_resource> resource; -}; - -template <typename T> -struct expression_pointer<T, false> : input_expression -{ - using value_type = T; - - expression_pointer() noexcept : instance(nullptr), vtable(nullptr) {} - expression_pointer(void* instance, const expression_vtable<T>* vtable) - : instance(instance), vtable(vtable) + CMT_INLINE bool substitute(expression_pointer<T>&& new_pointer, csize_t<0> = csize_t<0>{}) const { + return vtable->substitute(instance, std::move(new_pointer)); } - template <size_t N, KFR_ENABLE_IF(N <= maximum_expression_width)> - CMT_INLINE vec<T, N> operator()(cinput_t, size_t index, vec_t<T, N>) const - { - using func_t = void (*)(void*, size_t, vec<T, N>&); - static_assert(is_poweroftwo(N), "N must be a power of two"); - constexpr size_t findex = ilog2(N); - func_t func = reinterpret_cast<func_t>((*vtable)[3 + findex]); - vec<T, N> result; - func(instance, index, result); - return result; - } - template <size_t N, KFR_ENABLE_IF(N > maximum_expression_width)> - CMT_INLINE vec<T, N> operator()(cinput_t input, size_t index, vec_t<T, N>) const - { - return concat(operator()(cinput, index, vec_t<T, N / 2>()), - operator()(cinput, index + N / 2, vec_t<T, N / 2>())); - } - CMT_INLINE void begin_block(cinput_t, size_t size) const - { - using func_t = void (*)(void*, size_t); - func_t func = reinterpret_cast<func_t>((*vtable)[0]); - func(instance, size); - } - CMT_INLINE void end_block(cinput_t, size_t size) const - { - using func_t = void (*)(void*, size_t); - func_t func = reinterpret_cast<func_t>((*vtable)[1]); - func(instance, size); - } - CMT_INLINE size_t size() const - { - using func_t = size_t (*)(void*); - func_t func = reinterpret_cast<func_t>((*vtable)[2]); - return func(instance); - } + explicit operator bool() const { return instance != nullptr; } private: void* instance; const expression_vtable<T>* vtable; + std::shared_ptr<expression_resource> resource; }; namespace internal { -template <typename T, size_t N, typename Fn, typename Ret = vec<T, N>, - typename NonMemFn = void (*)(void*, size_t, Ret&)> -CMT_INLINE NonMemFn make_expression_func() -{ - return [](void* fn, size_t index, Ret& result) { - result = (reinterpret_cast<Fn*>(fn)->operator()(cinput, index, vec_t<T, N>())); - }; -} - -template <typename Fn, typename NonMemFn = void (*)(void*, size_t)> -CMT_INLINE NonMemFn make_expression_begin_block() -{ - return [](void* fn, size_t size) { reinterpret_cast<Fn*>(fn)->begin_block(cinput, size); }; -} -template <typename Fn, typename NonMemFn = void (*)(void*, size_t)> -CMT_INLINE NonMemFn make_expression_end_block() -{ - return [](void* fn, size_t size) { reinterpret_cast<Fn*>(fn)->end_block(cinput, size); }; -} -template <typename Fn, typename NonMemFn = size_t (*)(void*)> -CMT_INLINE NonMemFn make_expression_size() -{ - return [](void* fn) -> size_t { return reinterpret_cast<Fn*>(fn)->size(); }; -} - -template <typename T, typename E> -expression_vtable<T> make_expression_vtable_impl() -{ - expression_vtable<T> result; - - result[0] = reinterpret_cast<void*>(internal::make_expression_begin_block<decay<E>>()); - result[1] = reinterpret_cast<void*>(internal::make_expression_end_block<decay<E>>()); - result[2] = reinterpret_cast<void*>(internal::make_expression_size<decay<E>>()); - - cforeach(csizeseq_t<expression_vtable_size - 3>(), [&](auto u) { - constexpr size_t N = 1 << val_of(decltype(u)()); - result[3 + val_of(decltype(u)())] = - reinterpret_cast<void*>(internal::make_expression_func<T, N, decay<E>>()); - }); - return result; -} template <typename T, typename E> CMT_INLINE expression_vtable<T>* make_expression_vtable() { static_assert(is_input_expression<E>::value, "E must be an expression"); - static expression_vtable<T> vtable = internal::make_expression_vtable_impl<T, E>(); + static expression_vtable<T> vtable{ ctype_t<decay<E>>{} }; return &vtable; } -} +} // namespace internal /** @brief Converts the given expression into an opaque object. * This overload takes reference to the expression. @@ -247,4 +210,92 @@ CMT_INLINE expression_pointer<T> to_pointer(E&& expr) std::shared_ptr<expression_resource> ptr = make_resource(std::move(expr)); return expression_pointer<T>(ptr->instance(), internal::make_expression_vtable<T, E>(), std::move(ptr)); } + +template <typename T, size_t key> +class expression_placeholder : public input_expression +{ +public: + using value_type = T; + expression_placeholder() noexcept = default; + template <typename U, size_t N> + CMT_INLINE vec<U, N> operator()(cinput_t, size_t index, vec_t<U, N>) const + { + return pointer ? cast<U>(pointer(cinput, index, vec_t<T, N>())) : 0; + } + expression_pointer<T> pointer; +}; + +template <typename T, size_t key = 0> +KFR_SINTRIN expression_placeholder<T, key> placeholder(csize_t<key> = csize_t<key>{}) +{ + return expression_placeholder<T, key>(); +} + +template <typename... Args> +KFR_SINTRIN bool substitute(input_expression&, Args&&...) +{ + return false; } + +namespace internal +{ +template <typename... Args, typename T, size_t key, size_t... indices> +KFR_SINTRIN bool substitute(internal::expression_base<Args...>& expr, expression_pointer<T>&& new_pointer, + csize_t<key>, std::index_sequence<indices...>); + +} + +template <typename T, size_t key = 0> +KFR_SINTRIN bool substitute(expression_placeholder<T, key>& expr, expression_pointer<T>&& new_pointer, + csize_t<key> = csize_t<key>{}) +{ + expr.pointer = std::move(new_pointer); + return true; +} + +template <typename... Args, typename T, size_t key = 0> +KFR_SINTRIN bool substitute(internal::expression_base<Args...>& expr, expression_pointer<T>&& new_pointer, + csize_t<key> = csize_t<key>{}) +{ + return internal::substitute(expr, std::move(new_pointer), csize_t<key>{}, + std::index_sequence_for<Args...>{}); +} + +template <typename T, size_t key = 0> +KFR_SINTRIN bool substitute(expression_pointer<T>& expr, expression_pointer<T>&& new_pointer, + csize_t<key> = csize_t<key>{}) +{ + return expr.substitute(std::move(new_pointer), csize_t<key>{}); +} + +namespace internal +{ + +KFR_SINTRIN bool var_or() { return false; } + +template <typename... Args> +KFR_SINTRIN bool var_or(bool b, Args... args) +{ + return b || var_or(args...); +} + +template <typename... Args, typename T, size_t key, size_t... indices> +KFR_SINTRIN bool substitute(internal::expression_base<Args...>& expr, expression_pointer<T>&& new_pointer, + csize_t<key>, std::index_sequence<indices...>) +{ + return var_or(substitute(std::get<indices>(expr.args), std::move(new_pointer), csize_t<key>())...); +} + +} // namespace internal + +namespace internal +{ + +template <typename Expression, typename T, size_t key> +KFR_SINTRIN bool invoke_substitute(Expression& expr, expression_pointer<T>&& new_pointer, csize_t<key>) +{ + return kfr::substitute(expr, std::move(new_pointer), csize_t<key>{}); +} + +} // namespace internal +} // namespace kfr