Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ set(XSIMD_ALGORITHM_BENCHMARKS
bench_math.cpp
bench_map.cpp
bench_of.cpp
bench_find.cpp
)

add_executable(benchmark_xsimd_algorithm ${XSIMD_ALGORITHM_BENCHMARKS})
Expand Down
63 changes: 63 additions & 0 deletions benchmark/bench_find.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <algorithm>
#include <numeric>
#include <vector>

#include <benchmark/benchmark.h>

#include "xsimd_algorithm/stl/find.hpp"

#define BENCH_ARGS ->Arg(kSmall)->Arg(kMedium)->Arg(kLarge)

namespace
{
template <typename T>
std::vector<T> make_input(size_t size, size_t position, T constant)
{
std::vector<T> result(size);
std::iota(result.begin(), result.end(), T(1));
if (position < size)
{
result[position] = constant;
}
return result;
}

template <typename T>
void BM_std_find(benchmark::State& state)
{
const auto constant = T { -42 };
auto src = make_input<T>(static_cast<std::size_t>(state.range(0)), static_cast<std::size_t>(state.range(0)) - 1, constant);
for (auto _ : state)
{
auto result = std::find(src.begin(), src.end(), constant);
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * state.range(0));
}

template <typename T>
void BM_xsimd_find(benchmark::State& state)
{
const auto constant = T { -42 };
auto src = make_input<T>(static_cast<std::size_t>(state.range(0)), static_cast<std::size_t>(state.range(0)) - 1, constant);
for (auto _ : state)
{
auto result = xsimd::find(src.begin(), src.end(), constant);
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;

// find
BENCHMARK_TEMPLATE(BM_std_find, float)
BENCH_ARGS;
BENCHMARK_TEMPLATE(BM_xsimd_find, float)
BENCH_ARGS;

}
29 changes: 29 additions & 0 deletions include/xsimd_algorithm/stl/find.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/***************************************************************************
* 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_FIND_HPP
#define XSIMD_ALGORITHMS_FIND_HPP

#include "xsimd/xsimd.hpp"
#include "xsimd_algorithm/stl/find_if.hpp"

#include <iostream>
#include <iterator>

namespace xsimd
{
template <class Arch = default_arch, class ContiguousIterator, class T = typename std::iterator_traits<ContiguousIterator>::value_type>
ContiguousIterator find(ContiguousIterator first, ContiguousIterator last, const T& value) noexcept
{
return find_if<Arch>(first, last, [value](const auto& x) { return x == value; });
}
}
#endif // XSIMD_ALGORITHMS_FIND_HPP
90 changes: 90 additions & 0 deletions include/xsimd_algorithm/stl/find_if.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/***************************************************************************
* 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_FIND_IF_HPP
#define XSIMD_ALGORITHMS_FIND_IF_HPP

#include "xsimd/xsimd.hpp"

#include <iostream>
#include <iterator>

namespace xsimd
{
namespace detail
{
template <class Arch = default_arch, class ForwardIterator, class Predicate>
ForwardIterator sequential_find_if(ForwardIterator first, ForwardIterator last, Predicate&& p) noexcept
{
for (; first != last; ++first)
{
if (p(*first))
{
return first;
}
}
return last;
}
}

template <class Arch = default_arch, class ContiguousIterator, class Predicate>
ContiguousIterator find_if(ContiguousIterator first, ContiguousIterator last, Predicate&& p) noexcept
{
using value_type = typename std::decay<decltype(*first)>::type;
using batch_type = batch<value_type, Arch>;

const std::size_t size = static_cast<std::size_t>(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_find_if<Arch>(first, last, std::forward<Predicate>(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_it_result = detail::sequential_find_if<Arch>(first, align_begin_it, std::forward<Predicate>(p));

if (prologue_it_result!= align_begin_it)
{
return prologue_it_result;
}

// main loop: process aligned elements
for (auto current = align_begin_it; current != align_end_it; std::advance(current, simd_size))
{
batch_type batch_data = batch_type::load_aligned(&(*current));
auto offset = xsimd::countr_zero(p(batch_data));
if (offset != simd_size)
{
return std::next(current, offset);
}
}

// epilogue: process remaining elements
const auto epilogue_it_result = detail::sequential_find_if<Arch>(align_end_it, last, std::forward<Predicate>(p));
if (epilogue_it_result != last)
{
return epilogue_it_result;
}

return last;
}
}
#endif // XSIMD_ALGORITHMS_FIND_IF_HPP
30 changes: 30 additions & 0 deletions include/xsimd_algorithm/stl/find_if_not.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/***************************************************************************
* 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_FIND_IF_NOT_HPP
#define XSIMD_ALGORITHMS_FIND_IF_NOT_HPP

#include "xsimd/xsimd.hpp"
#include "xsimd_algorithm/stl/find_if.hpp"

#include <iostream>
#include <iterator>

namespace xsimd
{
template <class Arch = default_arch, class ContiguousIterator, class Predicate>
ContiguousIterator find_if_not(ContiguousIterator first, ContiguousIterator last, Predicate&& p) noexcept
{
return find_if<Arch>(first, last, [p](const auto& x) { return !p(x); });
}
}

#endif // XSIMD_ALGORITHMS_FIND_IF_NOT_HPP
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ set(XSIMD_ALGORITHM_TESTS
test_any_of.cpp
test_none_of.cpp
test_arange.cpp
test_find.cpp
test_find_if.cpp
test_find_if_not.cpp
test_map.cpp
test_iterator.cpp
test_math.cpp
Expand Down
101 changes: 101 additions & 0 deletions test/test_find.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/***************************************************************************
* 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/find.hpp"


#ifndef XSIMD_NO_SUPPORTED_ARCHITECTURE

#include "doctest/doctest.h"

#include <cstddef>
#include <numeric>
#include <vector>

#if XSIMD_WITH_NEON && !XSIMD_WITH_NEON64
#define FIND_TYPES int, float
#else
#define FIND_TYPES int, long, float, double
#endif


template <typename Type>
struct find_test
{
using vector = std::vector<Type>;
static constexpr std::size_t simd_size = xsimd::batch<Type>::size;

static vector fill_data(size_t size, size_t position, Type constant)
{
vector result(size);
std::iota(begin(result), end(result), Type(1));
if (position < size)
{
result[position] = constant;
}
return result;
}

void test_find_aligned( size_t size, Type constant= {-42}) const
{
const auto position = size / 2;
const vector data = fill_data(size, position, constant);

const auto result = xsimd::find(data.begin(), data.end(), constant);
const auto expected = std::find(data.begin(), data.end(), constant);
CHECK(result == expected);
}

void test_find_misaligned(size_t size, Type constant= {-42}) const
{
const auto position = size / 2;
const size_t missalignment = 1;
const vector data = fill_data(size + missalignment * 2, position, constant);
auto first = data.begin() + missalignment;
auto last = first + size;

const auto result = xsimd::find(first, last, constant);
const auto expected = std::find(first, last, constant);

CHECK(result == expected);
}
};

TEST_CASE_TEMPLATE("find test", T, FIND_TYPES)
{
using Test = find_test<T>;

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_find_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_find_misaligned(size);
}
}
}
#endif
Loading
Loading