diff --git a/CMakeLists.txt b/CMakeLists.txt index e310e1a50..82f0b20e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,6 @@ ${XSIMD_INCLUDE_DIR}/xsimd/config/xsimd_config.hpp ${XSIMD_INCLUDE_DIR}/xsimd/config/xsimd_cpuid.hpp ${XSIMD_INCLUDE_DIR}/xsimd/memory/xsimd_aligned_allocator.hpp ${XSIMD_INCLUDE_DIR}/xsimd/memory/xsimd_alignment.hpp -${XSIMD_INCLUDE_DIR}/xsimd/stl/algorithms.hpp ${XSIMD_INCLUDE_DIR}/xsimd/types/xsimd_all_registers.hpp ${XSIMD_INCLUDE_DIR}/xsimd/types/xsimd_api.hpp ${XSIMD_INCLUDE_DIR}/xsimd/types/xsimd_neon_register.hpp diff --git a/README.md b/README.md index e0840d957..6187e65d5 100644 --- a/README.md +++ b/README.md @@ -177,26 +177,6 @@ void mean(const vector_type& a, const vector_type& b, vector_type& res) } ``` -We also implement STL algorithms to work optimally on batches. Using `xsimd::transform` -the loop from the example becomes: - -```cpp -#include -#include -#include "xsimd/xsimd.hpp" -#include "xsimd/stl/algorithms.hpp" - -namespace xs = xsimd; -using vector_type = std::vector>; - -void mean(const vector_type& a, const vector_type& b, vector_type& res) -{ - xsimd::transform(a.begin(), a.end(), b.begin(), res.begin(), - [](const auto& x, const auto& y) { (x + y) / 2.; }); -} -``` - - ## Building and Running the Tests Building the tests requires the [GTest](https://github.com/google/googletest) testing framework and [cmake](https://cmake.org). diff --git a/include/xsimd/stl/algorithms.hpp b/include/xsimd/stl/algorithms.hpp deleted file mode 100644 index 5dcd55efc..000000000 --- a/include/xsimd/stl/algorithms.hpp +++ /dev/null @@ -1,201 +0,0 @@ -/*************************************************************************** - * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and * - * Martin Renou * - * Copyright (c) QuantStack * - * Copyright (c) Serge Guelton * - * * - * Distributed under the terms of the BSD 3-Clause License. * - * * - * The full license is in the file LICENSE, distributed with this software. * - ****************************************************************************/ - -#ifndef XSIMD_ALGORITHMS_HPP -#define XSIMD_ALGORITHMS_HPP - -#include -#include -#include -#include - -#include "../types/xsimd_api.hpp" - -namespace xsimd -{ - template - void transform(I1 first, I2 last, O1 out_first, UF&& f) noexcept - { - using value_type = typename std::decay::type; - using batch_type = batch; - - std::size_t size = static_cast(std::distance(first, last)); - std::size_t simd_size = batch_type::size; - - const auto* ptr_begin = &(*first); - auto* ptr_out = &(*out_first); - - std::size_t align_begin = xsimd::get_alignment_offset(ptr_begin, size, simd_size); - std::size_t out_align = xsimd::get_alignment_offset(ptr_out, size, simd_size); - std::size_t align_end = align_begin + ((size - align_begin) & ~(simd_size - 1)); - - if (align_begin == out_align) - { - for (std::size_t i = 0; i < align_begin; ++i) - { - out_first[i] = f(first[i]); - } - - for (std::size_t i = align_begin; i < align_end; i += simd_size) - { - batch_type batch = batch_type::load_aligned(&first[i]); - f(batch).store_aligned(&out_first[i]); - } - - for (std::size_t i = align_end; i < size; ++i) - { - out_first[i] = f(first[i]); - } - } - else - { - for (std::size_t i = 0; i < align_begin; ++i) - { - out_first[i] = f(first[i]); - } - - for (std::size_t i = align_begin; i < align_end; i += simd_size) - { - batch_type batch = batch_type::load_aligned(&first[i]); - f(batch).store_unaligned(&out_first[i]); - } - - for (std::size_t i = align_end; i < size; ++i) - { - out_first[i] = f(first[i]); - } - } - } - - template - void transform(I1 first_1, I2 last_1, I3 first_2, O1 out_first, UF&& f) noexcept - { - using value_type = typename std::decay::type; - using batch_type = batch; - - std::size_t size = static_cast(std::distance(first_1, last_1)); - std::size_t simd_size = batch_type::size; - - const auto* ptr_begin_1 = &(*first_1); - const auto* ptr_begin_2 = &(*first_2); - auto* ptr_out = &(*out_first); - - std::size_t align_begin_1 = xsimd::get_alignment_offset(ptr_begin_1, size, simd_size); - std::size_t align_begin_2 = xsimd::get_alignment_offset(ptr_begin_2, size, simd_size); - std::size_t out_align = xsimd::get_alignment_offset(ptr_out, size, simd_size); - std::size_t align_end = align_begin_1 + ((size - align_begin_1) & ~(simd_size - 1)); - -#define XSIMD_LOOP_MACRO(A1, A2, A3) \ - for (std::size_t i = 0; i < align_begin_1; ++i) \ - { \ - out_first[i] = f(first_1[i], first_2[i]); \ - } \ - \ - batch_type batch_1, batch_2; \ - for (std::size_t i = align_begin_1; i < align_end; i += simd_size) \ - { \ - batch_1 = batch_type::A1(&first_1[i]); \ - batch_2 = batch_type::A2(&first_2[i]); \ - f(batch_1, batch_2).A3(&out_first[i]); \ - } \ - \ - for (std::size_t i = align_end; i < size; ++i) \ - { \ - out_first[i] = f(first_1[i], first_2[i]); \ - } - - if (align_begin_1 == out_align && align_begin_1 == align_begin_2) - { - XSIMD_LOOP_MACRO(load_aligned, load_aligned, store_aligned); - } - else if (align_begin_1 == out_align && align_begin_1 != align_begin_2) - { - XSIMD_LOOP_MACRO(load_aligned, load_unaligned, store_aligned); - } - else if (align_begin_1 != out_align && align_begin_1 == align_begin_2) - { - XSIMD_LOOP_MACRO(load_aligned, load_aligned, store_unaligned); - } - else if (align_begin_1 != out_align && align_begin_1 != align_begin_2) - { - XSIMD_LOOP_MACRO(load_aligned, load_unaligned, store_unaligned); - } - -#undef XSIMD_LOOP_MACRO - } - - // TODO: Remove this once we drop C++11 support - namespace detail - { - struct plus - { - template - auto operator()(X&& x, Y&& y) noexcept -> decltype(x + y) { return x + y; } - }; - } - - template - Init reduce(Iterator1 first, Iterator2 last, Init init, BinaryFunction&& binfun = detail::plus {}) noexcept - { - using value_type = typename std::decay::type; - using batch_type = batch; - - std::size_t size = static_cast(std::distance(first, last)); - constexpr std::size_t simd_size = batch_type::size; - - if (size < simd_size) - { - while (first != last) - { - init = binfun(init, *first++); - } - return init; - } - - const auto* const ptr_begin = &(*first); - - std::size_t align_begin = xsimd::get_alignment_offset(ptr_begin, size, simd_size); - std::size_t align_end = align_begin + ((size - align_begin) & ~(simd_size - 1)); - - // reduce initial unaligned part - for (std::size_t i = 0; i < align_begin; ++i) - { - init = binfun(init, first[i]); - } - - // reduce aligned part - auto ptr = ptr_begin + align_begin; - batch_type batch_init = batch_type::load_aligned(ptr); - ptr += simd_size; - for (auto const end = ptr_begin + align_end; ptr < end; ptr += simd_size) - { - batch_type batch = batch_type::load_aligned(ptr); - batch_init = binfun(batch_init, batch); - } - - // reduce across batch - alignas(batch_type) std::array arr; - xsimd::store_aligned(arr.data(), batch_init); - for (auto x : arr) - init = binfun(init, x); - - // reduce final unaligned part - for (std::size_t i = align_end; i < size; ++i) - { - init = binfun(init, first[i]); - } - - return init; - } - -} - -#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c3561995c..3f5283a1e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -163,7 +163,6 @@ include_directories(${GTEST_INCLUDE_DIRS}) set(XSIMD_TESTS main.cpp - test_algorithms.cpp test_api.cpp test_arch.cpp test_basic_math.cpp diff --git a/test/test_algorithms.cpp b/test/test_algorithms.cpp deleted file mode 100644 index ae04373aa..000000000 --- a/test/test_algorithms.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/*************************************************************************** - * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and * - * Martin Renou * - * Copyright (c) QuantStack * - * Copyright (c) Serge Guelton * - * * - * Distributed under the terms of the BSD 3-Clause License. * - * * - * The full license is in the file LICENSE, distributed with this software. * - ****************************************************************************/ - -#include "xsimd/xsimd.hpp" -#ifndef XSIMD_NO_SUPPORTED_ARCHITECTURE - -#include "test_utils.hpp" -#include "xsimd/stl/algorithms.hpp" -#include - -struct binary_functor -{ - template - T operator()(const T& a, const T& b) const - { - return a + b; - } -}; - -struct unary_functor -{ - template - T operator()(const T& a) const - { - return -a; - } -}; - -template -class algorithms : public testing::Test -{ -public: - using vector = std::vector; - using aligned_vector = std::vector>; -}; - -#if XSIMD_WITH_NEON && !XSIMD_WITH_NEON64 -using algorithms_types = ::testing::Types>; -#else -using algorithms_types = ::testing::Types, std::complex>; -#endif - -TYPED_TEST_SUITE(algorithms, algorithms_types); - -TYPED_TEST(algorithms, binary_transform) -{ - using vector = typename TestFixture::vector; - using aligned_vector = typename TestFixture::aligned_vector; - - vector expected(93); - vector a(93, 123), b(93, 123), c(93); - aligned_vector aa(93, 123), ba(93, 123), ca(93); - - std::transform(a.begin(), a.end(), b.begin(), expected.begin(), - binary_functor {}); - - xsimd::transform(a.begin(), a.end(), b.begin(), c.begin(), - binary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), c.begin()) && expected.size() == c.size()); - std::fill(c.begin(), c.end(), -1); // erase - - xsimd::transform(aa.begin(), aa.end(), ba.begin(), c.begin(), - binary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), c.begin()) && expected.size() == c.size()); - std::fill(c.begin(), c.end(), -1); // erase - - xsimd::transform(aa.begin(), aa.end(), b.begin(), c.begin(), - binary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), c.begin()) && expected.size() == c.size()); - std::fill(c.begin(), c.end(), -1); // erase - - xsimd::transform(a.begin(), a.end(), ba.begin(), c.begin(), - binary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), c.begin()) && expected.size() == c.size()); - std::fill(c.begin(), c.end(), -1); // erase - - xsimd::transform(aa.begin(), aa.end(), ba.begin(), ca.begin(), - binary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), ca.begin()) && expected.size() == ca.size()); - std::fill(ca.begin(), ca.end(), -1); // erase - - xsimd::transform(aa.begin(), aa.end(), b.begin(), ca.begin(), - binary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), ca.begin()) && expected.size() == ca.size()); - std::fill(ca.begin(), ca.end(), -1); // erase - - xsimd::transform(a.begin(), a.end(), ba.begin(), ca.begin(), - binary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), ca.begin()) && expected.size() == ca.size()); - std::fill(ca.begin(), ca.end(), -1); // erase -} - -TYPED_TEST(algorithms, unary_transform) -{ - using vector = typename TestFixture::vector; - using aligned_vector = typename TestFixture::aligned_vector; - - vector expected(93); - vector a(93, 123), c(93); - aligned_vector aa(93, 123), ca(93); - - std::transform(a.begin(), a.end(), expected.begin(), - unary_functor {}); - - xsimd::transform(a.begin(), a.end(), c.begin(), - unary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), c.begin()) && expected.size() == c.size()); - std::fill(c.begin(), c.end(), -1); // erase - - xsimd::transform(aa.begin(), aa.end(), c.begin(), - unary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), c.begin()) && expected.size() == c.size()); - std::fill(c.begin(), c.end(), -1); // erase - - xsimd::transform(a.begin(), a.end(), ca.begin(), - unary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), ca.begin()) && expected.size() == ca.size()); - std::fill(ca.begin(), ca.end(), -1); // erase - - xsimd::transform(aa.begin(), aa.end(), ca.begin(), - unary_functor {}); - EXPECT_TRUE(std::equal(expected.begin(), expected.end(), ca.begin()) && expected.size() == ca.size()); - std::fill(ca.begin(), ca.end(), -1); // erase -} - -template -using test_allocator_type = xsimd::aligned_allocator; - -#if XSIMD_WITH_NEON && !XSIMD_WITH_NEON64 -using test_value_type = float; -#else -using test_value_type = double; -#endif - -class xsimd_reduce : public ::testing::Test -{ -public: - using aligned_vec_t = std::vector>; - - static constexpr std::size_t num_elements = 4 * xsimd::batch::size; - static constexpr std::size_t small_num = xsimd::batch::size - 1; - - aligned_vec_t vec = aligned_vec_t(num_elements, 123.); - aligned_vec_t small_vec = aligned_vec_t(small_num, 42.); - test_value_type init = 1337.; - - struct multiply - { - template - T operator()(const T& a, const T& b) const - { - return a * b; - } - }; -}; - -TEST_F(xsimd_reduce, unaligned_begin_unaligned_end) -{ - auto const begin = std::next(vec.begin()); - auto const end = std::prev(vec.end()); - - EXPECT_EQ(std::accumulate(begin, end, init), xsimd::reduce(begin, end, init)); - - if (small_vec.size() > 1) - { - auto const sbegin = std::next(small_vec.begin()); - auto const send = std::prev(small_vec.end()); - - EXPECT_EQ(std::accumulate(sbegin, send, init), xsimd::reduce(sbegin, send, init)); - } -} - -TEST_F(xsimd_reduce, unaligned_begin_aligned_end) -{ - auto const begin = std::next(vec.begin()); - auto const end = vec.end(); - - EXPECT_EQ(std::accumulate(begin, end, init), xsimd::reduce(begin, end, init)); - - if (small_vec.size() > 1) - { - auto const sbegin = std::next(small_vec.begin()); - auto const send = small_vec.end(); - - EXPECT_EQ(std::accumulate(sbegin, send, init), xsimd::reduce(sbegin, send, init)); - } -} - -TEST_F(xsimd_reduce, aligned_begin_unaligned_end) -{ - auto const begin = vec.begin(); - auto const end = std::prev(vec.end()); - - EXPECT_EQ(std::accumulate(begin, end, init), xsimd::reduce(begin, end, init)); - - if (small_vec.size() > 1) - { - auto const sbegin = small_vec.begin(); - auto const send = std::prev(small_vec.end()); - - EXPECT_EQ(std::accumulate(sbegin, send, init), xsimd::reduce(sbegin, send, init)); - } -} - -TEST_F(xsimd_reduce, aligned_begin_aligned_end) -{ - auto const begin = vec.begin(); - auto const end = vec.end(); - - EXPECT_EQ(std::accumulate(begin, end, init), xsimd::reduce(begin, end, init)); - - if (small_vec.size() > 1) - { - auto const sbegin = small_vec.begin(); - auto const send = small_vec.end(); - - EXPECT_EQ(std::accumulate(sbegin, send, init), xsimd::reduce(sbegin, send, init)); - } -} - -TEST_F(xsimd_reduce, using_custom_binary_function) -{ - auto const begin = vec.begin(); - auto const end = vec.end(); - - if (std::is_same::value) - { - EXPECT_DOUBLE_EQ(std::accumulate(begin, end, init, multiply {}), xsimd::reduce(begin, end, init, multiply {})); - } - else - { - EXPECT_FLOAT_EQ(std::accumulate(begin, end, init, multiply {}), xsimd::reduce(begin, end, init, multiply {})); - } - - if (small_vec.size() > 1) - { - auto const sbegin = small_vec.begin(); - auto const send = small_vec.end(); - - if (std::is_same::value) - { - EXPECT_DOUBLE_EQ(std::accumulate(sbegin, send, init, multiply {}), xsimd::reduce(sbegin, send, init, multiply {})); - } - else - { - EXPECT_FLOAT_EQ(std::accumulate(sbegin, send, init, multiply {}), xsimd::reduce(sbegin, send, init, multiply {})); - } - } -} - -#if XSIMD_X86_INSTR_SET > XSIMD_VERSION_NUMBER_NOT_AVAILABLE || XSIMD_ARM_INSTR_SET > XSIMD_VERSION_NUMBER_NOT_AVAILABLE -TEST(algorithms, iterator) -{ - std::vector> a(10 * 16, 0.2), b(1000, 2.), c(1000, 3.); - - std::iota(a.begin(), a.end(), 0.f); - std::vector a_cpy(a.begin(), a.end()); - - using batch_type = xsimd::batch; - auto begin = xsimd::aligned_iterator(&a[0]); - auto end = xsimd::aligned_iterator(&a[0] + a.size()); - - for (; begin != end; ++begin) - { - *begin = *begin / 2.f; - } - - for (auto& el : a_cpy) - { - el /= 2.f; - } - - EXPECT_TRUE(a.size() == a_cpy.size() && std::equal(a.begin(), a.end(), a_cpy.begin())); - - begin = xsimd::aligned_iterator(&a[0]); - *begin = sin(*begin); - - for (std::size_t i = 0; i < batch_type::size; ++i) - { - EXPECT_NEAR(a[i], sinf(a_cpy[i]), 1e-6); - } - -#if !XSIMD_WITH_NEON || XSIMD_WITH_NEON64 - std::vector, test_allocator_type>> ca(10 * 16, std::complex(0.2)); - using cbatch_type = xsimd::batch_type < std::complex; - auto cbegin = xsimd::aligned_iterator(&ca[0]); - auto cend = xsimd::aligned_iterator(&ca[0] + a.size()); - - for (; cbegin != cend; ++cbegin) - { - *cbegin = (*cbegin + std::complex(0, .3)) / 2.; - } - - cbegin = xsimd::aligned_iterator(&ca[0]); - *cbegin = sin(*cbegin); - *cbegin = sqrt(*cbegin); - auto real_part = abs(*(cbegin)); - (void)real_part; -#endif -} -#endif -#endif