From 4bbc5fab95e9182346126667d508c6a6de24f15c Mon Sep 17 00:00:00 2001 From: Denis Yaroshevskiy Date: Sat, 8 May 2021 19:55:56 +0100 Subject: [PATCH 1/3] FIX-690: preprocess range --- include/eve/algo/README.md | 7 ++ include/eve/algo/preprocess_range.hpp | 57 ++++++++++++++ test/unit/algo/CMakeLists.txt | 1 + test/unit/algo/preprocess_range.cpp | 103 ++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 include/eve/algo/preprocess_range.hpp create mode 100644 test/unit/algo/preprocess_range.cpp diff --git a/include/eve/algo/README.md b/include/eve/algo/README.md index 20f8b9c5d0..4497babaed 100644 --- a/include/eve/algo/README.md +++ b/include/eve/algo/README.md @@ -56,3 +56,10 @@ iterator that can represent any position in the underlying range. * `unaligned_ptr_iterator` A pointer + cardinal with the `iterator` interface. + +### preprocess range + +* `preprocess_range` + +Given a more general notion of a range + traits returns enhanced traits + +iterator/sentinel pair that is understood by eve. diff --git a/include/eve/algo/preprocess_range.hpp b/include/eve/algo/preprocess_range.hpp new file mode 100644 index 0000000000..b09004d322 --- /dev/null +++ b/include/eve/algo/preprocess_range.hpp @@ -0,0 +1,57 @@ +//================================================================================================== +/** + EVE - Expressive Vector Engine + Copyright : EVE Contributors & Maintainers + SPDX-License-Identifier: MIT +**/ +//================================================================================================== +#pragma once + +#include + +#include + +#include +#include + +namespace eve::algo +{ + namespace detail + { + template + struct preprocess_range_result + { + Traits traits; + I f; + S l; + }; + + template + preprocess_range_result(Traits, I, S) -> preprocess_range_result; + } + + struct preprocess_range_ + { + template + auto operator()(Traits traits, I f, S l) const + { + EVE_ASSERT(f != l, "preprocess_range requires a non-empty range"); + + auto* raw_f = &*f; + auto* raw_l = raw_f + (l - f); + using T = std::remove_reference_t; + using it = unaligned_ptr_iterator>>; + + return detail::preprocess_range_result{traits, it{raw_f}, it{raw_l}}; + } + + // Base case. Should validate that I, S are a valid iterator pair + template + auto operator()(Traits traits, I f, S l) const + { + EVE_ASSERT(f != l, "preprocess_range requires a non-empty range"); + return detail::preprocess_range_result{traits, f, l}; + } + + } inline constexpr preprocess_range; +} diff --git a/test/unit/algo/CMakeLists.txt b/test/unit/algo/CMakeLists.txt index 2fca00edb9..65f2e3f7c7 100644 --- a/test/unit/algo/CMakeLists.txt +++ b/test/unit/algo/CMakeLists.txt @@ -13,3 +13,4 @@ add_custom_target(unit.algo.simd.exe ) ##================================================================================================== ## ALGO tests make_unit("unit.algo" ptr_iterator.cpp) +make_unit("unit.algo" preprocess_range.cpp) diff --git a/test/unit/algo/preprocess_range.cpp b/test/unit/algo/preprocess_range.cpp new file mode 100644 index 0000000000..14d97ccaa6 --- /dev/null +++ b/test/unit/algo/preprocess_range.cpp @@ -0,0 +1,103 @@ +#include "algo_test.hpp" + +#include + +#include + +#include "iterator_conept_test.hpp" + +#include +#include + +namespace +{ + struct no_traits {}; +} + +EVE_TEST_TYPES("Check preprocess_range for contiguous iterators", algo_test::selected_types) +(eve::as_) +{ + using e_t = eve::element_type_t; + using N = eve::fixed>; + + alignas(sizeof(T)) std::array arr; + + std::vector vec(100u, 0); + + // pointers + { + auto [traits, f, l] = eve::algo::preprocess_range(no_traits{}, arr.begin(), arr.end()); + TTS_TYPE_IS(decltype(traits), no_traits); + TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); + TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); + TTS_EQUAL(f.ptr, arr.begin()); + TTS_EQUAL(l.ptr, arr.end()); + } + + // const pointers + { + auto [traits, f, l] = eve::algo::preprocess_range(no_traits{}, arr.cbegin(), arr.cend()); + TTS_TYPE_IS(decltype(traits), no_traits); + TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); + TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); + TTS_EQUAL(f.ptr, arr.cbegin()); + TTS_EQUAL(l.ptr, arr.cend()); + } + + // vector::iterator + { + auto [traits, f, l] = eve::algo::preprocess_range(no_traits{}, vec.begin(), vec.end()); + TTS_TYPE_IS(decltype(traits), no_traits); + TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); + TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); + TTS_EQUAL(f.ptr, vec.data()); + TTS_EQUAL(l.ptr, vec.data() + static_cast(vec.size())); + } + + // vector::const_iterator + { + auto [traits, f, l] = eve::algo::preprocess_range(no_traits{}, vec.cbegin(), vec.cend()); + TTS_TYPE_IS(decltype(traits), no_traits); + TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); + TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); + TTS_EQUAL(f.ptr, vec.data()); + TTS_EQUAL(l.ptr, vec.data() + static_cast(vec.size())); + } +}; + +EVE_TEST_TYPES("Check preprocess_range for eve ptr iterators", algo_test::selected_types) +(eve::as_) +{ + using e_t = eve::element_type_t; + using N = eve::fixed; + + alignas(sizeof(T)) std::array arr; + + auto run_one_test = [&](I _f, S _l) + { + auto [traits, f, l] = eve::algo::preprocess_range(no_traits{}, _f, _l); + TTS_TYPE_IS(decltype(traits), no_traits); + TTS_TYPE_IS(decltype(f), I); + TTS_TYPE_IS(decltype(l), S); + TTS_EQUAL(f, _f); + TTS_EQUAL(l, _l); + }; + + auto run_test = [&] (U* f, U* l) { + using aligned_it = eve::algo::aligned_ptr_iterator; + using aligned_p = typename aligned_it::aligned_ptr_type; + + eve::algo::unaligned_ptr_iterator u_f(f); + eve::algo::unaligned_ptr_iterator u_l(l); + eve::algo::aligned_ptr_iterator a_f(aligned_p{f}); + eve::algo::aligned_ptr_iterator a_l(aligned_p{l}); + + run_one_test(u_f, u_l); + run_one_test(u_f, a_l); + run_one_test(a_f, a_l); + run_one_test(a_f, u_l); + }; + + run_test(arr.begin(), arr.end()); + run_test(arr.cbegin(), arr.cend()); +}; From 79d906bf6d58c2a665c40cf4b48da75f3dfc7159 Mon Sep 17 00:00:00 2001 From: Denis Yaroshevskiy Date: Sat, 8 May 2021 23:05:24 +0100 Subject: [PATCH 2/3] FIX-690: any_of --- include/eve/algo/README.md | 24 +++++++++- include/eve/algo/any_of.hpp | 58 ++++++++++++++++++++++ include/eve/algo/for_each_iteration.hpp | 33 +++++++++++++ test/unit/algo/CMakeLists.txt | 6 ++- test/unit/algo/any_of.cpp | 23 +++++++++ test/unit/algo/find_generic_test.hpp | 64 +++++++++++++++++++++++++ test/unit/algo/preprocess_range.cpp | 28 ++++++----- 7 files changed, 223 insertions(+), 13 deletions(-) create mode 100644 include/eve/algo/any_of.hpp create mode 100644 include/eve/algo/for_each_iteration.hpp create mode 100644 test/unit/algo/any_of.cpp create mode 100644 test/unit/algo/find_generic_test.hpp diff --git a/include/eve/algo/README.md b/include/eve/algo/README.md index 4497babaed..4685a0f177 100644 --- a/include/eve/algo/README.md +++ b/include/eve/algo/README.md @@ -40,6 +40,15 @@ The main model is `unaligned_ptr_iterator` iterator that can represent any position in the underlying range. +### iteration pattern (concept) + +A reusable component to do `while(f != l) ++f;`. +Given traits, f, l, delegate calls the delegate for each piece with (f, ignore). +The specific api of the delegate varies by iteration pattern. + +Main one is `for_each_iteration`. +However for some algorithms, like `reverse` and maybe `partition` it's not a good. + ### concepts * `unaligned_t` -> a convinience to get the unaligned type. @@ -57,9 +66,22 @@ iterator that can represent any position in the underlying range. A pointer + cardinal with the `iterator` interface. -### preprocess range +### preprocess_range * `preprocess_range` Given a more general notion of a range + traits returns enhanced traits + iterator/sentinel pair that is understood by eve. + +### for_each_iteration + +`for_each_iteration(traits, f, l)` + +_TODO_: we need some way to know where the iteration pattern starts from before actually doing anything. Example is `remove` it needs the base of where to start writing and it has to be there at the time of delegate creation. + +The basic iteration pattern. A simd version of `while (f != l)`. +Delegate needs to implement `step(iterator, relative_condtional_expr)` and +_TODO_ `step_unrolled(iterator)` if the `unroll` trait is bigger than 1. +`iterator` type from steps is not guarantied to match `decltype(f)` (for when we know we can align the input for example). + +`step` returns `true` if it wants to break or `false` to continue. diff --git a/include/eve/algo/any_of.hpp b/include/eve/algo/any_of.hpp new file mode 100644 index 0000000000..b02e42c124 --- /dev/null +++ b/include/eve/algo/any_of.hpp @@ -0,0 +1,58 @@ +//================================================================================================== +/** + EVE - Expressive Vector Engine + Copyright : EVE Contributors & Maintainers + SPDX-License-Identifier: MIT +**/ +//================================================================================================== +#pragma once + +#include +#include + +#include + + + +namespace eve::algo +{ + struct any_of_ + { + struct default_traits {}; + + template + struct delegate + { + explicit delegate(P p) : p(p) {} + + bool step(auto it, eve::relative_conditional_expr auto ignore) + { + auto test = p(eve::load[ignore](it)); + res = eve::any[ignore](test); + return res; + } + + P p; + bool res = false; + }; + + template + bool operator()(Traits _traits, I _f, S _l, P p) const + { + if (_f == _l) return false; + + delegate d{p}; + + auto [traits, f, l] = preprocess_range(_traits, _f, _l); + for_each_iteration(traits, f, l, d); + return d.res; + } + + template + bool operator()(I f, S l, P p) const + { + return operator()(default_traits{}, f, l, p); + } + + } inline constexpr any_of; +} diff --git a/include/eve/algo/for_each_iteration.hpp b/include/eve/algo/for_each_iteration.hpp new file mode 100644 index 0000000000..c44562548f --- /dev/null +++ b/include/eve/algo/for_each_iteration.hpp @@ -0,0 +1,33 @@ +//================================================================================================== +/** + EVE - Expressive Vector Engine + Copyright : EVE Contributors & Maintainers + SPDX-License-Identifier: MIT +**/ +//================================================================================================== +#pragma once + +#include +#include + +namespace eve::algo +{ + struct for_each_iteration_ + { + template + void operator()(Traits, I f, S l, Delegate& delegate) const + { + static constexpr std::ptrdiff_t step = typename I::cardinal{}(); + + I precise_l = f + ((l - f) / step * step); + + while (f != precise_l) { + if (delegate.step(f, eve::ignore_none)) return; + f += step; + } + + eve::keep_first ignore{l - precise_l}; + delegate.step(f, ignore); + } + } inline constexpr for_each_iteration; +} diff --git a/test/unit/algo/CMakeLists.txt b/test/unit/algo/CMakeLists.txt index 65f2e3f7c7..f151fb03e3 100644 --- a/test/unit/algo/CMakeLists.txt +++ b/test/unit/algo/CMakeLists.txt @@ -12,5 +12,9 @@ add_custom_target(unit.algo.simd.exe ) ##================================================================================================== ## ALGO tests -make_unit("unit.algo" ptr_iterator.cpp) +# Helpers make_unit("unit.algo" preprocess_range.cpp) +make_unit("unit.algo" ptr_iterator.cpp) + +# Algorithms +make_unit("unit.algo" any_of.cpp) diff --git a/test/unit/algo/any_of.cpp b/test/unit/algo/any_of.cpp new file mode 100644 index 0000000000..e94afb93c8 --- /dev/null +++ b/test/unit/algo/any_of.cpp @@ -0,0 +1,23 @@ +//================================================================================================== +/** + EVE - Expressive Vector Engine + Copyright : EVE Contributors & Maintainers + SPDX-License-Identifier: MIT +**/ +//================================================================================================== + +#include "algo_test.hpp" + +#include + +#include "find_generic_test.hpp" + +EVE_TEST_TYPES("Check any_of", algo_test::selected_types) +(eve::as_ as_t) +{ + algo_test::find_generic_test(as_t, [](auto f, auto l, auto res) { + bool actual = eve::algo::any_of(f, l, [](auto x) { return x != 0; }); + bool expected = (res != l); + TTS_EQUAL(actual, expected); + }); +}; diff --git a/test/unit/algo/find_generic_test.hpp b/test/unit/algo/find_generic_test.hpp new file mode 100644 index 0000000000..5921d177b4 --- /dev/null +++ b/test/unit/algo/find_generic_test.hpp @@ -0,0 +1,64 @@ +//================================================================================================== +/** + EVE - Expressive Vector Engine + Copyright : EVE Contributors & Maintainers + SPDX-License-Identifier: MIT +**/ +//================================================================================================== +#pragma once + +#include "test.hpp" + +#include + +#include + +namespace algo_test +{ + template + void find_generic_test_page_ends(eve::as_, Test test) + { + using e_t = eve::element_type_t; + std::vector> page(4096 / sizeof(e_t), e_t{0}); + + const std::ptrdiff_t elemenents_to_test = 512 / T::static_size; + + auto f = page.begin(); + auto l = page.begin() + elemenents_to_test; + + auto run = [&] { + for (auto it = f; it < l; ++it) { + test(f, l, l); + *it = 1; + test(f, l, it); + *it = 0; + } + }; + + // from the beginning + while (f < l) { + run(); + *l = 1; + --l; + *f = 1; + ++f; + } + + std::fill(page.begin(), page.end(), 0); + + // from the end + while (f < l) { + run(); + *l = 1; + --l; + *f = 1; + ++f; + } + } + + template + void find_generic_test(eve::as_ as_t, Test test) + { + find_generic_test_page_ends(as_t, test); + } +} diff --git a/test/unit/algo/preprocess_range.cpp b/test/unit/algo/preprocess_range.cpp index 14d97ccaa6..e5504c717e 100644 --- a/test/unit/algo/preprocess_range.cpp +++ b/test/unit/algo/preprocess_range.cpp @@ -1,11 +1,17 @@ +//================================================================================================== +/** + EVE - Expressive Vector Engine + Copyright : EVE Contributors & Maintainers + SPDX-License-Identifier: MIT +**/ +//================================================================================================== + #include "algo_test.hpp" #include #include -#include "iterator_conept_test.hpp" - #include #include @@ -20,7 +26,7 @@ EVE_TEST_TYPES("Check preprocess_range for contiguous iterators", algo_test::sel using e_t = eve::element_type_t; using N = eve::fixed>; - alignas(sizeof(T)) std::array arr; + alignas(sizeof(T)) std::array arr{}; std::vector vec(100u, 0); @@ -30,8 +36,8 @@ EVE_TEST_TYPES("Check preprocess_range for contiguous iterators", algo_test::sel TTS_TYPE_IS(decltype(traits), no_traits); TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); - TTS_EQUAL(f.ptr, arr.begin()); - TTS_EQUAL(l.ptr, arr.end()); + TTS_EQUAL((void*)f.ptr, (void*)arr.begin()); + TTS_EQUAL((void*)l.ptr, (void*)arr.end()); } // const pointers @@ -40,8 +46,8 @@ EVE_TEST_TYPES("Check preprocess_range for contiguous iterators", algo_test::sel TTS_TYPE_IS(decltype(traits), no_traits); TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); - TTS_EQUAL(f.ptr, arr.cbegin()); - TTS_EQUAL(l.ptr, arr.cend()); + TTS_EQUAL((void*)f.ptr, (void*)arr.cbegin()); + TTS_EQUAL((void*)l.ptr, (void*)arr.cend()); } // vector::iterator @@ -50,8 +56,8 @@ EVE_TEST_TYPES("Check preprocess_range for contiguous iterators", algo_test::sel TTS_TYPE_IS(decltype(traits), no_traits); TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); - TTS_EQUAL(f.ptr, vec.data()); - TTS_EQUAL(l.ptr, vec.data() + static_cast(vec.size())); + TTS_EQUAL((void*)f.ptr, (void*)vec.data()); + TTS_EQUAL((void*)l.ptr, (void*)(vec.data() + static_cast(vec.size()))); } // vector::const_iterator @@ -60,8 +66,8 @@ EVE_TEST_TYPES("Check preprocess_range for contiguous iterators", algo_test::sel TTS_TYPE_IS(decltype(traits), no_traits); TTS_TYPE_IS(decltype(f), (eve::algo::unaligned_ptr_iterator)); TTS_TYPE_IS(decltype(l), (eve::algo::unaligned_ptr_iterator)); - TTS_EQUAL(f.ptr, vec.data()); - TTS_EQUAL(l.ptr, vec.data() + static_cast(vec.size())); + TTS_EQUAL((void*)f.ptr, (void*)vec.data()); + TTS_EQUAL((void*)l.ptr, (void*)(vec.data() + static_cast(vec.size()))); } }; From 132113703456e8fe391aba63acc43e94703dabf0 Mon Sep 17 00:00:00 2001 From: Denis Yaroshevskiy Date: Wed, 12 May 2021 23:03:35 +0100 Subject: [PATCH 3/3] FIX-690: tagged dispatch for ptr iterators --- include/eve/algo/ptr_iterator.hpp | 110 ++++++++++++++---------------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/include/eve/algo/ptr_iterator.hpp b/include/eve/algo/ptr_iterator.hpp index e2cbb0d837..4c15305b3b 100644 --- a/include/eve/algo/ptr_iterator.hpp +++ b/include/eve/algo/ptr_iterator.hpp @@ -19,6 +19,7 @@ namespace eve::algo struct unaligned_ptr_iterator : operations_with_distance { using cardinal = Cardinal; + using wide_value_type = eve::wide, cardinal>; unaligned_ptr_iterator() = default; explicit unaligned_ptr_iterator(T* ptr) : ptr(ptr) {} @@ -30,6 +31,31 @@ namespace eve::algo auto operator<=>(unaligned_ptr_iterator const&) const = default; + template + friend wide_value_type tagged_dispatch( eve::tag::load_, C cond, unaligned_ptr_iterator self ) + { + return eve::load[cond](self.ptr, cardinal{}); + } + + friend wide_value_type tagged_dispatch( eve::tag::load_, unaligned_ptr_iterator self ) + { + return eve::load(self.ptr, cardinal{}); + } + + template + friend void tagged_dispatch( + eve::tag::store_, C cond, wide_value_type v, unaligned_ptr_iterator self ) + requires (!std::is_const_v) + { + eve::store[cond](v, self.ptr); + } + + friend void tagged_dispatch( eve::tag::store_, wide_value_type v, unaligned_ptr_iterator self ) + requires ( !std::is_const_v ) + { + eve::store(v, self.ptr); + } + T* ptr; }; @@ -37,6 +63,8 @@ namespace eve::algo struct aligned_ptr_iterator : operations_with_distance, forward_to_unaligned { using cardinal = Cardinal; + using wide_value_type = eve::wide, cardinal>; + using aligned_ptr_type = eve::aligned_ptr; aligned_ptr_iterator(); @@ -45,63 +73,31 @@ namespace eve::algo auto unaligned() const { return unaligned_ptr_iterator{ptr.get()}; } aligned_ptr_iterator& operator+=(std::ptrdiff_t n) { ptr += n; return *this; } + template + friend wide_value_type tagged_dispatch( eve::tag::load_, C cond, aligned_ptr_iterator self ) + { + return eve::load[cond](self.ptr, cardinal{}); + } + + friend wide_value_type tagged_dispatch( eve::tag::load_, aligned_ptr_iterator self ) + { + return eve::load(self.ptr, cardinal{}); + } + + template + friend void tagged_dispatch( + eve::tag::store_, C cond, wide_value_type v, aligned_ptr_iterator self ) + requires ( !std::is_const_v ) + { + return eve::store[cond](v, self.ptr); + } + + friend void tagged_dispatch( eve::tag::store_, wide_value_type v, aligned_ptr_iterator self ) + requires ( !std::is_const_v ) + { + return eve::store(v, self.ptr); + } + aligned_ptr_type ptr; }; } - -// TODO: after #683 replace with a proper hook -namespace eve::detail -{ - template - EVE_FORCEINLINE auto load_(EVE_SUPPORTS(cpu_), C cond, algo::aligned_ptr_iterator it) { - return eve::load[cond](it.ptr, N{}); - } - - template - EVE_FORCEINLINE auto load_(EVE_SUPPORTS(cpu_), algo::aligned_ptr_iterator it) { - return eve::load(it.ptr, N{}); - } - - - template - EVE_FORCEINLINE auto load_(EVE_SUPPORTS(cpu_), C cond, algo::unaligned_ptr_iterator it) { - return eve::load[cond](it.ptr, N{}); - } - - template - EVE_FORCEINLINE auto load_(EVE_SUPPORTS(cpu_), algo::unaligned_ptr_iterator it) { - return eve::load(it.ptr, N{}); - } - - // --------------------------- - // store, we need to restrict the wide type but we can do that when the customization - // is avaliable. - template - requires std::invocable::aligned_ptr_type> - EVE_FORCEINLINE auto store_(EVE_SUPPORTS(cpu_), C cond, Wide wide, algo::aligned_ptr_iterator it) { - return eve::store[cond](wide, it.ptr); - } - - template - requires std::invocable::aligned_ptr_type> - EVE_FORCEINLINE auto store_(EVE_SUPPORTS(cpu_), Wide wide, algo::aligned_ptr_iterator it) { - return eve::store(wide, it.ptr); - } - - template - requires std::invocable - EVE_FORCEINLINE auto store_(EVE_SUPPORTS(cpu_), C cond, Wide wide, algo::unaligned_ptr_iterator it) { - return eve::store[cond](wide, it.ptr); - } - - template - requires std::invocable - EVE_FORCEINLINE auto store_(EVE_SUPPORTS(cpu_), Wide wide, algo::unaligned_ptr_iterator it) { - return eve::store(wide, it.ptr); - } -}