diff --git a/libcxx/benchmarks/algorithms/find.bench.cpp b/libcxx/benchmarks/algorithms/find.bench.cpp --- a/libcxx/benchmarks/algorithms/find.bench.cpp +++ b/libcxx/benchmarks/algorithms/find.bench.cpp @@ -9,12 +9,15 @@ #include #include #include +#include #include #include -template +template static void bm_find(benchmark::State& state) { - std::vector vec1(state.range(), '1'); + using T = Container::value_type; + + Container vec1(state.range(), '1'); std::mt19937_64 rng(std::random_device{}()); for (auto _ : state) { @@ -25,13 +28,18 @@ vec1[idx] = '1'; } } -BENCHMARK(bm_find)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_find)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_find)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -template +template static void bm_ranges_find(benchmark::State& state) { - std::vector vec1(state.range(), '1'); + using T = Container::value_type; + + Container vec1(state.range(), '1'); std::mt19937_64 rng(std::random_device{}()); for (auto _ : state) { @@ -42,9 +50,12 @@ vec1[idx] = '1'; } } -BENCHMARK(bm_ranges_find)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_ranges_find)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_ranges_find)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); +BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); static void bm_vector_bool_find(benchmark::State& state) { std::vector vec1(state.range(), false); diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -22,6 +22,7 @@ __algorithm/find_first_of.h __algorithm/find_if.h __algorithm/find_if_not.h + __algorithm/find_segment_if.h __algorithm/for_each.h __algorithm/for_each_n.h __algorithm/for_each_segment.h diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h --- a/libcxx/include/__algorithm/find.h +++ b/libcxx/include/__algorithm/find.h @@ -10,6 +10,7 @@ #ifndef _LIBCPP___ALGORITHM_FIND_H #define _LIBCPP___ALGORITHM_FIND_H +#include <__algorithm/find_segment_if.h> #include <__algorithm/min.h> #include <__algorithm/unwrap_iter.h> #include <__bit/countr.h> @@ -118,8 +119,35 @@ return std::__find_bool(__first, static_cast(__last - __first)); } +// segmented iterator implementation + +template +struct __find_segment; + +template ::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _SegmentedIterator +__find_impl(_SegmentedIterator __first, _SegmentedIterator __last, const _Tp& __value, _Proj& __proj) { + return std::__find_segment_if(std::move(__first), std::move(__last), __find_segment<_Tp>(__value), __proj); +} + +template +struct __find_segment { + const _Tp& __value_; + + __find_segment(const _Tp& __value) : __value_(__value) {} + + template + _InputIterator operator()(_InputIterator __first, _InputIterator __last, _Proj& __proj) { + return std::__find_impl(__first, __last, __value_, __proj); + } +}; + +// public API template -_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator +_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator find(_InputIterator __first, _InputIterator __last, const _Tp& __value) { __identity __proj; return std::__rewrap_iter( diff --git a/libcxx/include/__algorithm/find_segment_if.h b/libcxx/include/__algorithm/find_segment_if.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/find_segment_if.h @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_FIND_SEGMENT_IF_H +#define _LIBCPP___ALGORITHM_FIND_SEGMENT_IF_H + +#include <__config> +#include <__iterator/segmented_iterator.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _SegmentedIterator +__find_segment_if(_SegmentedIterator __first, _SegmentedIterator __last, _Pred __pred, _Proj __proj) { + using _Traits = __segmented_iterator_traits<_SegmentedIterator>; + + auto __sfirst = _Traits::__segment(__first); + auto __slast = _Traits::__segment(__last); + + // We are in a single segment, so we might not be at the beginning or end + if (__sfirst == __slast) + return _Traits::__compose(__sfirst, __pred(_Traits::__local(__first), _Traits::__local(__last), __proj)); + + { // We have more than one segment. Itertor over the first segment, since we might not start at the beginning + auto __llast = _Traits::__end(__sfirst); + auto __liter = __pred(_Traits::__local(__first), __llast, __proj); + if (__liter != __llast) + return _Traits::__compose(__sfirst, __liter); + } + ++__sfirst; + + // Iterate over the segments which are guaranteed to be completely in the range + while (__sfirst != __slast) { + auto __llast = _Traits::__end(__sfirst); + auto __liter = __pred(_Traits::__begin(__sfirst), _Traits::__end(__sfirst), __proj); + if (__liter != __llast) + return _Traits::__compose(__sfirst, __liter); + ++__sfirst; + } + + // Iterate over the last segment + return _Traits::__compose(__sfirst, __pred(_Traits::__begin(__sfirst), _Traits::__local(__last), __proj)); +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ALGORITHM_FIND_SEGMENT_IF_H diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp --- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -113,6 +114,49 @@ } }; +void test_deque() { + { // empty deque + std::deque data; + assert(std::find(data.begin(), data.end(), 4) == data.end()); + } + + { // single element - match + std::deque data = {4}; + assert(std::find(data.begin(), data.end(), 4) == data.begin()); + } + + { // single element - no match + std::deque data = {3}; + assert(std::find(data.begin(), data.end(), 4) == data.end()); + } + + // many elements + for (auto size : {2, 3, 1023, 1024, 1025, 2047, 2048, 2049}) { + { // last element match + std::deque data; + data.resize(size); + std::fill(data.begin(), data.end(), 3); + data[size - 1] = 4; + assert(std::find(data.begin(), data.end(), 4) == data.end() - 1); + } + + { // second-last element match + std::deque data; + data.resize(size); + std::fill(data.begin(), data.end(), 3); + data[size - 2] = 4; + assert(std::find(data.begin(), data.end(), 4) == data.end() - 2); + } + + { // no match + std::deque data; + data.resize(size); + std::fill(data.begin(), data.end(), 3); + assert(std::find(data.begin(), data.end(), 4) == data.end()); + } + } +} + TEST_CONSTEXPR_CXX20 bool test() { types::for_each(types::integer_types(), TestTypes()); types::for_each(types::integer_types(), TestTypes()); @@ -126,6 +170,7 @@ } int main(int, char**) { + test_deque(); test(); #if TEST_STD_VER >= 20 static_assert(test());