Skip to content

Commit 67edb6b

Browse files
author
retornaz-thomas
committed
[feature] implement min_element cf issue #20
1 parent 38b0211 commit 67edb6b

2 files changed

Lines changed: 233 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/***************************************************************************
2+
* Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and *
3+
* Martin Renou *
4+
* Copyright (c) QuantStack *
5+
* Copyright (c) Serge Guelton *
6+
* *
7+
* Distributed under the terms of the BSD 3-Clause License. *
8+
* *
9+
* The full license is in the file LICENSE, distributed with this software. *
10+
****************************************************************************/
11+
12+
#ifndef XSIMD_ALGORITHMS_MIN_ELEMENT_HPP
13+
#define XSIMD_ALGORITHMS_MIN_ELEMENT_HPP
14+
15+
#include "xsimd/xsimd.hpp"
16+
17+
#include <iterator>
18+
19+
namespace xsimd
20+
{
21+
namespace detail
22+
{
23+
template <class Arch = default_arch, class ForwardIterator, class Compare>
24+
ForwardIterator sequential_min_element(ForwardIterator first, ForwardIterator last, Compare&& comp) noexcept
25+
{
26+
if (first == last)
27+
{
28+
return last;
29+
}
30+
31+
ForwardIterator min_it = first;
32+
33+
for (++first; first != last; ++first)
34+
{
35+
if (comp(*min_it, *first))
36+
{
37+
min_it = first;
38+
}
39+
}
40+
41+
return min_it;
42+
}
43+
}
44+
45+
template <class Arch = default_arch, class ContiguousIterator, class Compare>
46+
ContiguousIterator min_element(ContiguousIterator first, ContiguousIterator last, Compare&& comp) noexcept
47+
{
48+
using value_type = typename std::decay<decltype(*first)>::type;
49+
using batch_type = batch<value_type, Arch>;
50+
51+
const std::size_t size = static_cast<std::size_t>(std::distance(first, last));
52+
constexpr std::size_t simd_size = batch_type::size;
53+
54+
// Quick return for small ranges
55+
if (size < simd_size)
56+
{
57+
return detail::sequential_min_element(first, last, std::forward<Compare>(comp));
58+
}
59+
60+
// prologue: process elements until the first aligned one
61+
// compute alignment offsets
62+
const auto* const ptr_begin = &(*first);
63+
const std::size_t align_begin = xsimd::get_alignment_offset(ptr_begin, size, simd_size);
64+
const std::size_t align_end = align_begin + ((size - align_begin) & ~(simd_size - 1));
65+
66+
const auto align_begin_it = std::next(first, align_begin);
67+
const auto align_end_it = std::next(first, align_end);
68+
69+
auto min_it = detail::sequential_min_element(first, align_begin_it, std::forward<Compare>(comp));
70+
auto current_min = *min_it;
71+
auto current_min_index = std::distance(first, min_it);
72+
73+
// Generate a batch of the current min value
74+
batch_type current_min_batch = batch_type(current_min);
75+
76+
// main loop: process aligned elements
77+
for (auto current = align_begin_it; current != align_end_it; std::advance(current, simd_size))
78+
{
79+
batch_type batch_val = batch_type::load_aligned(&(*current));
80+
81+
// Compare the current batch with the current min batch
82+
auto mask = comp(current_min_batch, batch_val);
83+
84+
// Update ?
85+
if (xsimd::any(mask))
86+
{
87+
// Update the current min batch
88+
current_min_batch = xsimd::select(mask, batch_val, current_min_batch);
89+
// extract the min value
90+
current_min = xsimd::reduce_min(current_min_batch);
91+
//retrieve position of the min value in the current batch
92+
auto min_pos = std::find(current, current + simd_size, current_min);
93+
if (min_pos != current + simd_size)
94+
{
95+
current_min_index = std::distance(first, min_pos);
96+
}
97+
}
98+
}
99+
100+
min_it = std::next(first, current_min_index);
101+
102+
for (auto current = align_end_it; current != last; ++current)
103+
{
104+
if (comp(current_min, *current))
105+
{
106+
current_min = *current;
107+
min_it = current;
108+
}
109+
}
110+
return min_it;
111+
}
112+
113+
template <class Arch = default_arch, class ContiguousIterator>
114+
ContiguousIterator min_element(ContiguousIterator first, ContiguousIterator last) noexcept
115+
{
116+
return xsimd::min_element<Arch>(first, last, [](const auto& lhs, const auto& rhs) { return lhs > rhs; });
117+
}
118+
119+
} // namespace xsimd
120+
121+
#endif // XSIMD_ALGORITHMS_MIN_ELEMENT_HPP

‎test/test_min_element.cpp‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/***************************************************************************
2+
* Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and *
3+
* Martin Renou *
4+
* Copyright (c) QuantStack *
5+
* Copyright (c) Serge Guelton *
6+
* *
7+
* Distributed under the terms of the BSD 3-Clause License. *
8+
* *
9+
* The full license is in the file LICENSE, distributed with this software. *
10+
****************************************************************************/
11+
12+
#include "xsimd_algorithm/stl/min_element.hpp"
13+
14+
#ifndef XSIMD_NO_SUPPORTED_ARCHITECTURE
15+
16+
#include "doctest/doctest.h"
17+
18+
#include <cstddef>
19+
#include <vector>
20+
21+
#if XSIMD_WITH_NEON && !XSIMD_WITH_NEON64
22+
#define MIN_ELEMENT_TYPES int, float
23+
#else
24+
#define MIN_ELEMENT_TYPES int, long, float, double
25+
#endif
26+
27+
template <typename Type>
28+
struct min_element_test
29+
{
30+
using vector = std::vector<Type>;
31+
static constexpr std::size_t simd_size = xsimd::batch<Type>::size;
32+
33+
static vector fill_data(size_t size, Type min, size_t position)
34+
{
35+
vector result(size,Type(0));
36+
37+
if (position < size)
38+
{
39+
result[position] = min;
40+
}
41+
42+
return result;
43+
}
44+
45+
void test_min_element_aligned( size_t size, size_t position) const
46+
{
47+
const auto minimum = Type(-42);
48+
const vector data = fill_data(size, minimum, position);
49+
50+
const auto result = xsimd::min_element(data.begin(), data.end());
51+
const auto expected = std::min_element(data.begin(), data.end());
52+
53+
CHECK(result == expected);
54+
}
55+
56+
void test_min_element_misaligned(size_t size, size_t position) const
57+
{
58+
const auto minimum = Type(-42);
59+
const size_t missalignment = 1;
60+
const vector data = fill_data(size + missalignment * 2, minimum, position);
61+
auto first = data.begin() + missalignment;
62+
auto last = first + size;
63+
64+
const auto result = xsimd::min_element(first, last);
65+
const auto expected = std::min_element(first, last);
66+
67+
CHECK(result == expected);
68+
}
69+
};
70+
71+
TEST_CASE_TEMPLATE("min_element test", T, MIN_ELEMENT_TYPES)
72+
{
73+
using Test = min_element_test<T>;
74+
75+
const std::array test_sizes {
76+
size_t { 0 },
77+
size_t { 1 },
78+
Test::simd_size - 1,
79+
Test::simd_size,
80+
Test ::simd_size + 1,
81+
Test ::simd_size *4,
82+
};
83+
84+
const std::array positions {
85+
size_t { 0 },
86+
size_t { 1 },
87+
Test::simd_size / 4,
88+
Test::simd_size / 2,
89+
Test::simd_size - 1,
90+
};
91+
92+
Test test;
93+
94+
size_t sub_case_id = 0;
95+
for (const auto size : test_sizes)
96+
{
97+
for (const auto position : positions)
98+
{
99+
const std::string aligned_subcase_name = "Aligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size) + ", position: " + std::to_string(position);
100+
SUBCASE(aligned_subcase_name.c_str())
101+
{
102+
test.test_min_element_aligned(size, position);
103+
}
104+
const std::string misaligned_subcase_name = "Misaligned" + std::to_string(sub_case_id++) + ", size: " + std::to_string(size) + ", position: " + std::to_string(position);
105+
SUBCASE(misaligned_subcase_name.c_str())
106+
{
107+
test.test_min_element_misaligned(size, position);
108+
}
109+
}
110+
}
111+
}
112+
#endif

0 commit comments

Comments
 (0)