diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index e214e36..3bf0b19 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -20,6 +20,7 @@ set(XSIMD_ALGORITHM_BENCHMARKS main.cpp bench_math.cpp bench_map.cpp + bench_of.cpp ) add_executable(benchmark_xsimd_algorithm ${XSIMD_ALGORITHM_BENCHMARKS}) diff --git a/benchmark/bench_of.cpp b/benchmark/bench_of.cpp new file mode 100644 index 0000000..d2d15ba --- /dev/null +++ b/benchmark/bench_of.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +#include "xsimd_algorithm/stl/all_of.hpp" +#include "xsimd_algorithm/stl/any_of.hpp" +#include "xsimd_algorithm/stl/none_of.hpp" + +#define BENCH_ARGS ->Arg(kSmall)->Arg(kMedium)->Arg(kLarge) + +namespace +{ + template + std::vector make_input(std::size_t n, T constant) + { + std::vector v(n); + std::fill(v.begin(), v.end(), constant); + return v; + } + + template + void BM_std_all_of(benchmark::State& state) + { + const auto constant = T { 5 }; + + auto src = make_input(static_cast(state.range(0)), constant); + for (auto _ : state) + { + auto result = std::all_of(src.begin(), src.end(), [constant](const auto& x) { return x == constant; }); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); + } + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + + template + void BM_xsimd_all_of(benchmark::State& state) + { + const auto constant = T { 5 }; + auto src = make_input(static_cast(state.range(0)), constant); + for (auto _ : state) + { + auto result = xsimd::all_of(src.begin(), src.end(), [constant](const auto& x) { return x == constant; }); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); + } + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + + template + void BM_std_any_of(benchmark::State& state) + { + const auto constant = T { 5 }; + const auto tested = T { 10 }; + auto src = make_input(static_cast(state.range(0)), constant); + for (auto _ : state) + { + auto result = std::any_of(src.begin(), src.end(), [tested](const auto& x) { return x == tested; }); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); + } + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + + template + void BM_xsimd_any_of(benchmark::State& state) + { + const auto constant = T { 5 }; + const auto tested = T { 10 }; + auto src = make_input(static_cast(state.range(0)), constant); + for (auto _ : state) + { + auto result = xsimd::any_of(src.begin(), src.end(), [tested](const auto& x) { return x == tested; }); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); + } + state.SetItemsProcessed(state.iterations() * state.range(0)); + } + + static constexpr long long kSmall = 1LL << 12; + static constexpr long long kMedium = 1LL << 18; + static constexpr long long kLarge = 1LL << 22; + + // all_of + BENCHMARK_TEMPLATE(BM_std_all_of, float) + BENCH_ARGS; + BENCHMARK_TEMPLATE(BM_xsimd_all_of, float) + BENCH_ARGS; + // any_of + BENCHMARK_TEMPLATE(BM_std_any_of, float) + BENCH_ARGS; + BENCHMARK_TEMPLATE(BM_xsimd_any_of, float) + BENCH_ARGS; + +} \ No newline at end of file diff --git a/include/xsimd_algorithm/algorithms.hpp b/include/xsimd_algorithm/algorithms.hpp index 454f975..21d03f2 100644 --- a/include/xsimd_algorithm/algorithms.hpp +++ b/include/xsimd_algorithm/algorithms.hpp @@ -12,6 +12,10 @@ #ifndef XSIMD_ALGORITHMS_HPP #define XSIMD_ALGORITHMS_HPP +#include "xsimd_algorithm/stl/all_of.hpp" +#include "xsimd_algorithm/stl/any_of.hpp" +#include "xsimd_algorithm/stl/arange.hpp" +//#include "xsimd_algorithm/stl/none_of.hpp" #include "xsimd_algorithm/stl/reduce.hpp" #include "xsimd_algorithm/stl/transform.hpp" diff --git a/include/xsimd_algorithm/stl/all_of.hpp b/include/xsimd_algorithm/stl/all_of.hpp new file mode 100644 index 0000000..da0b2ea --- /dev/null +++ b/include/xsimd_algorithm/stl/all_of.hpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * 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_ALL_OF_HPP +#define XSIMD_ALGORITHMS_ALL_OF_HPP + +#include "xsimd/xsimd.hpp" + +#include + +namespace xsimd +{ + namespace detail + { + template + bool sequential_all_of(ForwardIterator first, ForwardIterator last, Predicate&& p) noexcept + { + for (; first != last; ++first) + { + if (!p(*first)) + { + return false; + } + } + return true; + } + } + + template + bool all_of(ContiguousIterator first, ContiguousIterator last, Predicate&& p) noexcept + { + using value_type = typename std::decay::type; + using batch_type = batch; + + const std::size_t size = static_cast(std::distance(first, last)); + constexpr std::size_t simd_size = batch_type::size; + + // quick exit for small sizes + if (size < simd_size) + { + return detail::sequential_all_of(first, last, std::forward(p)); + } + + // prologue: process elements until the first aligned one + // compute alignment offsets + const auto* const ptr_begin = &(*first); + const std::size_t align_begin = xsimd::get_alignment_offset(ptr_begin, size, simd_size); + const std::size_t align_end = align_begin + ((size - align_begin) & ~(simd_size - 1)); + + const auto align_begin_it = std::next(first, align_begin); + const auto align_end_it = std::next(first, align_end); + + const auto prologue_result = detail::sequential_all_of(first, align_begin_it, std::forward(p)); + if (!prologue_result) + { + return false; + } + // main loop: process aligned elements + for (auto current = align_begin_it; current != align_end_it; std::advance(current, simd_size)) + { + batch_type batch_val = batch_type::load_aligned(&(*current)); + if (!xsimd::all(p(batch_val))) + { + return false; + } + } + // epilogue: process remaining elements + const auto epilogue_result = detail::sequential_all_of(align_end_it, last, std::forward(p)); + if (!epilogue_result) + { + return false; + } + + return true; + } + +} + +#endif // XSIMD_ALGORITHMS_ALL_OF_HPP \ No newline at end of file diff --git a/include/xsimd_algorithm/stl/any_of.hpp b/include/xsimd_algorithm/stl/any_of.hpp new file mode 100644 index 0000000..75f5eed --- /dev/null +++ b/include/xsimd_algorithm/stl/any_of.hpp @@ -0,0 +1,88 @@ +/*************************************************************************** + * 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_ANY_OF_HPP +#define XSIMD_ALGORITHMS_ANY_OF_HPP + +#include "xsimd/xsimd.hpp" + +#include + +namespace xsimd +{ + namespace detail + { + template + bool sequential_any_of(ForwardIterator first, ForwardIterator last, Predicate&& p) noexcept + { + for (; first != last; ++first) + { + if (p(*first)) + { + return true; + } + } + return false; + } + } + + template + bool any_of(ContiguousIterator first, ContiguousIterator last, Predicate&& p) noexcept + { + using value_type = typename std::decay::type; + using batch_type = batch; + + const std::size_t size = static_cast(std::distance(first, last)); + constexpr std::size_t simd_size = batch_type::size; + + // quick exit for small sizes + if (size < simd_size) + { + return detail::sequential_any_of(first, last, std::forward(p)); + } + + // prologue: process elements until the first aligned one + // compute alignment offsets + const auto* const ptr_begin = &(*first); + const std::size_t align_begin = xsimd::get_alignment_offset(ptr_begin, size, simd_size); + const std::size_t align_end = align_begin + ((size - align_begin) & ~(simd_size - 1)); + + const auto align_begin_it = std::next(first, align_begin); + const auto align_end_it = std::next(first, align_end); + + const auto prologue_result = detail::sequential_any_of(first, align_begin_it, std::forward(p)); + if (prologue_result) + { + return true; + } + + // main loop: process aligned elements + for (auto current = align_begin_it; current != align_end_it; std::advance(current, simd_size)) + { + batch_type batch_val = batch_type::load_aligned(&(*current)); + if (xsimd::any(p(batch_val))) + { + return true; + } + } + // epilogue: process remaining elements + const auto epilogue_result = detail::sequential_any_of(align_end_it, last, std::forward(p)); + if (epilogue_result) + { + return true; + } + + return false; + } + +} + +#endif // XSIMD_ALGORITHMS_ANY_OF_HPP \ No newline at end of file diff --git a/include/xsimd_algorithm/stl/none_of.hpp b/include/xsimd_algorithm/stl/none_of.hpp new file mode 100644 index 0000000..c315497 --- /dev/null +++ b/include/xsimd_algorithm/stl/none_of.hpp @@ -0,0 +1,31 @@ +/*************************************************************************** + * 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_NONE_OF_HPP +#define XSIMD_ALGORITHMS_NONE_OF_HPP + +#include "xsimd/xsimd.hpp" +#include "xsimd_algorithm/stl/any_of.hpp" + +#include + +namespace xsimd +{ + + template + bool none_of(ContiguousIterator first, ContiguousIterator last, Predicate&& p) noexcept + { + return !any_of(first, last, std::forward(p)); + } + +} + +#endif // XSIMD_ALGORITHMS_NONE_OF_HPP \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7aba53b..86e6b9b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -47,6 +47,9 @@ endif() set(XSIMD_ALGORITHM_TESTS main.cpp + test_all_of.cpp + test_any_of.cpp + test_none_of.cpp test_arange.cpp test_map.cpp test_iterator.cpp diff --git a/test/test_all_of.cpp b/test/test_all_of.cpp new file mode 100644 index 0000000..32c5f34 --- /dev/null +++ b/test/test_all_of.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * 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_algorithm/stl/all_of.hpp" + +#ifndef XSIMD_NO_SUPPORTED_ARCHITECTURE + +#include "doctest/doctest.h" + +#include +#include + +#if XSIMD_WITH_NEON && !XSIMD_WITH_NEON64 +#define ALL_OF_TYPES int, float +#else +#define ALL_OF_TYPES int, long, float, double +#endif + +template +struct all_of_test +{ + using vector = std::vector; + static constexpr std::size_t simd_size = xsimd::batch::size; + + static vector fill_data(size_t size, Type constant) + { + vector result(size); + + for (size_t i = 0; i < size; ++i) + { + result[i] = constant; + } + + return result; + } + + void test_all_of_aligned( size_t size, Type constant= {5}) const + { + const vector data = fill_data(size, constant); + + const auto result = xsimd::all_of(data.begin(), data.end(), [constant](const auto& x) { return x == constant; }); + + CHECK(result == true); + } + + void test_all_of_misaligned(size_t size, Type constant= {5}) const + { + const size_t missalignment = 1; + const vector data = fill_data(size + missalignment * 2, constant); + auto first = data.begin() + missalignment; + auto last = first + size; + + const auto result = xsimd::all_of(first, last, [constant](const auto& x) { return x == constant; }); + + CHECK(result == true); + } +}; + +TEST_CASE_TEMPLATE("all_of test", T, ALL_OF_TYPES) +{ + using Test = all_of_test; + + const std::array test_sizes { + size_t { 0 }, + size_t { 1 }, + Test::simd_size - 1, + Test::simd_size, + Test ::simd_size + 1, + }; + + Test test; + + size_t sub_case_id = 0; + for (const auto size : test_sizes) + { + const std::string aligned_subcase_name = "Aligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size); + SUBCASE(aligned_subcase_name.c_str()) + { + test.test_all_of_aligned(size); + } + const std::string misaligned_subcase_name = "Misaligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size); + SUBCASE(misaligned_subcase_name.c_str()) + { + test.test_all_of_misaligned(size); + } + } +} +#endif \ No newline at end of file diff --git a/test/test_any_of.cpp b/test/test_any_of.cpp new file mode 100644 index 0000000..765cb07 --- /dev/null +++ b/test/test_any_of.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * 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_algorithm/stl/any_of.hpp" + +#ifndef XSIMD_NO_SUPPORTED_ARCHITECTURE + +#include "doctest/doctest.h" + +#include +#include + +#if XSIMD_WITH_NEON && !XSIMD_WITH_NEON64 +#define ANY_OF_TYPES int, float +#else +#define ANY_OF_TYPES int, long, float, double +#endif + +template +struct any_of_test +{ + using vector = std::vector; + static constexpr std::size_t simd_size = xsimd::batch::size; + + static vector fill_data(size_t size, Type constant) + { + vector result(size); + + for (size_t i = 0; i < size; ++i) + { + result[i] = constant; + } + + return result; + } + + void test_any_of_aligned( size_t size, Type constant= {5}, Type tested= {10}) const + { + const vector data = fill_data(size, constant); + + const auto result = xsimd::any_of(data.begin(), data.end(), [tested](const auto& x) { return x == tested; }); + + CHECK(result == false); + } + + void test_any_of_misaligned(size_t size, Type constant= {5}, Type tested= {10}) const + { + const size_t missalignment = 1; + const vector data = fill_data(size + missalignment * 2, constant); + auto first = data.begin() + missalignment; + auto last = first + size; + + const auto result = xsimd::any_of(first, last, [tested](const auto& x) { return x == tested; }); + + CHECK(result == false); + } +}; + +TEST_CASE_TEMPLATE("any_of test", T, ANY_OF_TYPES) +{ + using Test = any_of_test; + + const std::array test_sizes { + size_t { 0 }, + size_t { 1 }, + Test::simd_size - 1, + Test::simd_size, + Test ::simd_size + 1, + }; + + Test test; + + size_t sub_case_id = 0; + for (const auto size : test_sizes) + { + const std::string aligned_subcase_name = "Aligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size); + SUBCASE(aligned_subcase_name.c_str()) + { + test.test_any_of_aligned(size); + } + const std::string misaligned_subcase_name = "Misaligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size); + SUBCASE(misaligned_subcase_name.c_str()) + { + test.test_any_of_misaligned(size); + } + } +} +#endif \ No newline at end of file diff --git a/test/test_none_of.cpp b/test/test_none_of.cpp new file mode 100644 index 0000000..0a753be --- /dev/null +++ b/test/test_none_of.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * 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_algorithm/stl/none_of.hpp" + +#ifndef XSIMD_NO_SUPPORTED_ARCHITECTURE + +#include "doctest/doctest.h" + +#include +#include + +#if XSIMD_WITH_NEON && !XSIMD_WITH_NEON64 +#define NONE_OF_TYPES int, float +#else +#define NONE_OF_TYPES int, long, float, double +#endif + +template +struct none_of_test +{ + using vector = std::vector; + static constexpr std::size_t simd_size = xsimd::batch::size; + + static vector fill_data(size_t size, Type constant) + { + vector result(size); + + for (size_t i = 0; i < size; ++i) + { + result[i] = constant; + } + + return result; + } + + void test_none_of_aligned( size_t size, Type constant= {5}, Type tested= {10}) const + { + const vector data = fill_data(size, constant); + + const auto result = xsimd::none_of(data.begin(), data.end(), [tested](const auto& x) { return x == tested; }); + + CHECK(result == true); + } + + void test_none_of_misaligned(size_t size, Type constant= {5}, Type tested= {10}) const + { + const size_t missalignment = 1; + const vector data = fill_data(size + missalignment * 2, constant); + auto first = data.begin() + missalignment; + auto last = first + size; + + const auto result = xsimd::none_of(first, last, [tested](const auto& x) { return x == tested; }); + + CHECK(result == true); + } +}; + +TEST_CASE_TEMPLATE("none_of test", T, NONE_OF_TYPES) +{ + using Test = none_of_test; + + const std::array test_sizes { + size_t { 0 }, + size_t { 1 }, + Test::simd_size - 1, + Test::simd_size, + Test ::simd_size + 1, + }; + + Test test; + + size_t sub_case_id = 0; + for (const auto size : test_sizes) + { + const std::string aligned_subcase_name = "Aligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size); + SUBCASE(aligned_subcase_name.c_str()) + { + test.test_none_of_aligned(size); + } + const std::string misaligned_subcase_name = "Misaligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size); + SUBCASE(misaligned_subcase_name.c_str()) + { + test.test_none_of_misaligned(size); + } + } +} +#endif \ No newline at end of file