Skip to content

Commit d4cc80f

Browse files
1 parent 520f1d2 commit d4cc80f

File tree

5 files changed

+508
-70
lines changed

5 files changed

+508
-70
lines changed

benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ add_benchmark(search src/search.cpp)
128128
add_benchmark(std_copy src/std_copy.cpp)
129129
add_benchmark(sv_equal src/sv_equal.cpp)
130130
add_benchmark(swap_ranges src/swap_ranges.cpp)
131+
add_benchmark(unique src/unique.cpp)
131132

132133
add_benchmark(vector_bool_copy src/std/containers/sequences/vector.bool/copy/test.cpp)
133134
add_benchmark(vector_bool_copy_n src/std/containers/sequences/vector.bool/copy_n/test.cpp)

benchmarks/src/unique.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <algorithm>
5+
#include <benchmark/benchmark.h>
6+
#include <cstdint>
7+
#include <random>
8+
#include <type_traits>
9+
#include <vector>
10+
11+
#include "skewed_allocator.hpp"
12+
13+
enum class alg_type { std_fn, rng };
14+
15+
template <alg_type Type, class T>
16+
void u(benchmark::State& state) {
17+
std::mt19937_64 gen(22033);
18+
using TD = std::conditional_t<sizeof(T) == 1, int, T>;
19+
std::binomial_distribution<TD> dis(5);
20+
21+
std::vector<T, not_highly_aligned_allocator<T>> src(2552);
22+
std::generate(src.begin(), src.end(), [&] { return static_cast<T>(dis(gen)); });
23+
24+
std::vector<T, not_highly_aligned_allocator<T>> v;
25+
v.reserve(src.size());
26+
for (auto _ : state) {
27+
v = src;
28+
benchmark::DoNotOptimize(v);
29+
if constexpr (Type == alg_type::std_fn) {
30+
benchmark::DoNotOptimize(std::unique(v.begin(), v.end()));
31+
} else {
32+
benchmark::DoNotOptimize(std::ranges::unique(v));
33+
}
34+
}
35+
}
36+
37+
BENCHMARK(u<alg_type::std_fn, std::uint8_t>);
38+
BENCHMARK(u<alg_type::std_fn, std::uint16_t>);
39+
BENCHMARK(u<alg_type::std_fn, std::uint32_t>);
40+
BENCHMARK(u<alg_type::std_fn, std::uint64_t>);
41+
42+
BENCHMARK(u<alg_type::rng, std::uint8_t>);
43+
BENCHMARK(u<alg_type::rng, std::uint16_t>);
44+
BENCHMARK(u<alg_type::rng, std::uint32_t>);
45+
BENCHMARK(u<alg_type::rng, std::uint64_t>);
46+
47+
BENCHMARK_MAIN();

stl/inc/algorithm

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ __declspec(noalias) void __stdcall __std_replace_4(
7979
void* _First, void* _Last, uint32_t _Old_val, uint32_t _New_val) noexcept;
8080
__declspec(noalias) void __stdcall __std_replace_8(
8181
void* _First, void* _Last, uint64_t _Old_val, uint64_t _New_val) noexcept;
82+
83+
void* __stdcall __std_unique_1(void* _First, void* _Last) noexcept;
84+
void* __stdcall __std_unique_2(void* _First, void* _Last) noexcept;
85+
void* __stdcall __std_unique_4(void* _First, void* _Last) noexcept;
86+
void* __stdcall __std_unique_8(void* _First, void* _Last) noexcept;
8287
} // extern "C"
8388

8489
_STD_BEGIN
@@ -205,6 +210,21 @@ __declspec(noalias) void _Replace_vectorized(
205210
}
206211
}
207212

213+
template <class _Ty>
214+
_Ty* _Unique_vectorized(_Ty* const _First, _Ty* const _Last) noexcept {
215+
if constexpr (sizeof(_Ty) == 1) {
216+
return reinterpret_cast<_Ty*>(::__std_unique_1(_First, _Last));
217+
} else if constexpr (sizeof(_Ty) == 2) {
218+
return reinterpret_cast<_Ty*>(::__std_unique_2(_First, _Last));
219+
} else if constexpr (sizeof(_Ty) == 4) {
220+
return reinterpret_cast<_Ty*>(::__std_unique_4(_First, _Last));
221+
} else if constexpr (sizeof(_Ty) == 8) {
222+
return reinterpret_cast<_Ty*>(::__std_unique_8(_First, _Last));
223+
} else {
224+
_STL_INTERNAL_STATIC_ASSERT(false); // Unexpected size
225+
}
226+
}
227+
208228
// Can we activate the vector algorithms for find_first_of?
209229
template <class _It1, class _It2, class _Pr>
210230
constexpr bool _Vector_alg_in_find_first_of_is_safe = _Equal_memcmp_is_safe<_It1, _It2, _Pr>;
@@ -219,6 +239,10 @@ template <class _Iter, class _Ty1, class _Ty2>
219239
constexpr bool _Vector_alg_in_ranges_replace_is_safe =
220240
_Vector_alg_in_replace_is_safe<_Iter, _Ty1> // can search and replace
221241
&& _Vector_alg_in_find_is_safe_elem<_Ty2, _Iter_value_t<_Iter>>; // replacement fits
242+
243+
// Can we activate the vector algorithms for unique?
244+
template <class _Iter, class _Pr>
245+
constexpr bool _Vector_alg_in_unique_is_safe = _Equal_memcmp_is_safe<_Iter, _Iter, _Pr>;
222246
_STD_END
223247
#endif // _USE_STD_VECTOR_ALGORITHMS
224248

@@ -4869,6 +4893,25 @@ _NODISCARD_UNIQUE_ALG _CONSTEXPR20 _FwdIt unique(_FwdIt _First, _FwdIt _Last, _P
48694893
_STD _Adl_verify_range(_First, _Last);
48704894
auto _UFirst = _STD _Get_unwrapped(_First);
48714895
const auto _ULast = _STD _Get_unwrapped(_Last);
4896+
4897+
#if _USE_STD_VECTOR_ALGORITHMS
4898+
if constexpr (_Vector_alg_in_unique_is_safe<decltype(_UFirst), _Pr>) {
4899+
if (!_STD _Is_constant_evaluated()) {
4900+
const auto _First_ptr = _STD _To_address(_UFirst);
4901+
const auto _Result = _STD _Unique_vectorized(_First_ptr, _STD _To_address(_ULast));
4902+
4903+
if constexpr (is_pointer_v<decltype(_UFirst)>) {
4904+
_UFirst = _Result;
4905+
} else {
4906+
_UFirst += _Result - _First_ptr;
4907+
}
4908+
4909+
_STD _Seek_wrapped(_Last, _UFirst);
4910+
return _Last;
4911+
}
4912+
}
4913+
#endif // _USE_STD_VECTOR_ALGORITHMS
4914+
48724915
if (_UFirst != _ULast) {
48734916
for (auto _UFirstb = _UFirst; ++_UFirst != _ULast; _UFirstb = _UFirst) {
48744917
if (_Pred(*_UFirstb, *_UFirst)) { // copy down
@@ -4945,6 +4988,24 @@ namespace ranges {
49454988
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
49464989
_STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>);
49474990

4991+
#if _USE_STD_VECTOR_ALGORITHMS
4992+
if constexpr (is_same_v<_Pj, identity> && sized_sentinel_for<_Se, _It>
4993+
&& _Vector_alg_in_unique_is_safe<_It, _Pr>) {
4994+
if (!_STD is_constant_evaluated()) {
4995+
const auto _Size = _Last - _First;
4996+
const auto _First_ptr = _STD to_address(_First);
4997+
const auto _Last_ptr = _First_ptr + static_cast<size_t>(_Size);
4998+
const auto _Result = _STD _Unique_vectorized(_First_ptr, _Last_ptr);
4999+
5000+
if constexpr (is_pointer_v<_It>) {
5001+
return {_Result, _Last_ptr};
5002+
} else {
5003+
return {_First + (_Result - _First_ptr), _First + _Size};
5004+
}
5005+
}
5006+
}
5007+
#endif // _USE_STD_VECTOR_ALGORITHMS
5008+
49485009
auto _Current = _First;
49495010
if (_First == _Last) {
49505011
return {_STD move(_Current), _STD move(_First)};

0 commit comments

Comments
 (0)