kfrlib / kfr

Fast, modern C++ DSP framework, FFT, Sample Rate Conversion, FIR/IIR/Biquad Filters (SSE, AVX, AVX-512, ARM NEON)
https://www.kfrlib.com
GNU General Public License v2.0
1.64k stars 252 forks source link

impact of using KFR_USE_STD_ALLOCATION #146

Closed mipac closed 10 months ago

mipac commented 2 years ago

it would be great to use std type like vector or complex how can we define a list of tests to measure the performance or precision differences ?

dancazarin commented 1 year ago

Upcoming KFR 5.x will accept any user or standard type as KFR expression as long as a special trait class is implemented. So using std::vector in any context where an expression or univector is required will be perfectly possible.

dancazarin commented 10 months ago

There are no precision differences for complex and vector types in KFR and std. Runtime performance should be very similar too. The only difference is that univector uses cache-aligned allocation (which is good for SIMD too) and contains some helper functions and classes (assigning from expressions, operators etc). The memory layout is exactly the same as in std::vector<T, kfr::data_allocator<T>>, so one can reinterpret_cast it to vector type and vice-versa. Strictly speaking it's an UB but works very well with all compilers in the wild. With KFR_USE_STD_ALLOCATION kfr::data_allocator becomes an alias for std::allocator<T>, so univector becomes a class derived from a regular std::vector<T>. But regardless of all above in KFR 5 you can add expressions semantics (and SIMD performance) for any type, including custom containers etc.

This is the example of doing this for std::array

// should be in kfr namespace
template <typename T, size_t N1>
struct expression_traits<std::array<T, N1>> : expression_traits_defaults
{
    using value_type             = T;
    constexpr static size_t dims = 1;

    constexpr static shape<1> get_shape(const std::array<T, N1>& self) { return shape<1>{ N1 }; }
    constexpr static shape<1> get_shape() { return shape<1>{ N1 }; }

    template <index_t Axis, size_t N>
    friend KFR_INTRINSIC vec<T, N> get_elements(const std::array<T, N1>& CMT_RESTRICT self, const shape<1>& index,
                                                const axis_params<Axis, N>&)
    {
        const T* CMT_RESTRICT const data = self.data();
        return read<N>(data + index[0]);
    }
    template <index_t Axis, size_t N>
    friend KFR_INTRINSIC void set_elements(std::array<T, N1>& CMT_RESTRICT self, const shape<1>& index,
                                           const axis_params<Axis, N>&, const identity<vec<T, N>>& val)
    {
        T* CMT_RESTRICT const data = self.data();
        write(data + index[0], val);
    }
};