Skip to content

Commit 59c96c8

Browse files
authored
[oneDPL][tests] Add ranges::partial_sort_copy (#2780)
1 parent 6e83069 commit 59c96c8

8 files changed

Lines changed: 166 additions & 20 deletions

include/oneapi/dpl/pstl/algorithm_impl.h

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,7 +2525,7 @@ __pattern_partial_sort_copy(_Tag, _ExecutionPolicy&&, _ForwardIterator __first,
25252525
{
25262526
static_assert(__is_serial_tag_v<_Tag> || __is_parallel_forward_tag_v<_Tag>);
25272527

2528-
return ::std::partial_sort_copy(__first, __last, __d_first, __d_last, __comp);
2528+
return std::partial_sort_copy(__first, __last, __d_first, __d_last, __comp);
25292529
}
25302530

25312531
template <class _IsVector, class _ExecutionPolicy, class _RandomAccessIterator1, class _RandomAccessIterator2,
@@ -2547,56 +2547,55 @@ __pattern_partial_sort_copy(__parallel_tag<_IsVector>, _ExecutionPolicy&& __exec
25472547
if (__n2 >= __n1)
25482548
{
25492549
__par_backend::__parallel_stable_sort(
2550-
__backend_tag{}, ::std::forward<_ExecutionPolicy>(__exec), __d_first, __d_first + __n1, __comp,
2550+
__backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __d_first, __d_first + __n1, __comp,
25512551
[__first, __d_first](_RandomAccessIterator2 __i, _RandomAccessIterator2 __j, _Compare __comp) {
25522552
_RandomAccessIterator1 __i1 = __first + (__i - __d_first);
25532553
_RandomAccessIterator1 __j1 = __first + (__j - __d_first);
25542554

25552555
// 1. Copy elements from input to output
25562556
__brick_copy<__parallel_tag<_IsVector>>{}(__i1, __j1, __i, _IsVector{});
25572557
// 2. Sort elements in output sequence
2558-
::std::sort(__i, __j, __comp);
2558+
std::sort(__i, __j, __comp);
25592559
},
25602560
__n1);
25612561
return __d_first + __n1;
25622562
}
25632563
else
25642564
{
2565-
using _T1 = typename std::iterator_traits<_RandomAccessIterator1>::value_type;
25662565
using _T2 = typename std::iterator_traits<_RandomAccessIterator2>::value_type;
2567-
__par_backend::__buffer<_T1> __buf(__n1);
2568-
_T1* __r = __buf.get();
2566+
__par_backend::__buffer<_T2> __buf(__n1);
2567+
_T2* __r = __buf.get();
25692568

25702569
__par_backend::__parallel_stable_sort(
2571-
__backend_tag{}, ::std::forward<_ExecutionPolicy>(__exec), __r, __r + __n1, __comp,
2572-
[__n2, __first, __r](_T1* __i, _T1* __j, _Compare __comp) {
2570+
__backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __r, __r + __n1, __comp,
2571+
[__n2, __first, __r](_T2* __i, _T2* __j, _Compare __comp) {
25732572
_RandomAccessIterator1 __it = __first + (__i - __r);
25742573

25752574
// 1. Copy elements from input to raw memory
2576-
for (_T1* __k = __i; __k != __j; ++__k, (void)++__it)
2575+
for (_T2* __k = __i; __k != __j; ++__k, (void)++__it)
25772576
{
25782577
::new (__k) _T2(*__it);
25792578
}
25802579

25812580
// 2. Sort elements in temporary buffer
25822581
if (__n2 < __j - __i)
2583-
::std::partial_sort(__i, __i + __n2, __j, __comp);
2582+
std::partial_sort(__i, __i + __n2, __j, __comp);
25842583
else
2585-
::std::sort(__i, __j, __comp);
2584+
std::sort(__i, __j, __comp);
25862585
},
25872586
__n2);
25882587

25892588
// 3. Move elements from temporary buffer to output
2590-
__par_backend::__parallel_for(__backend_tag{}, ::std::forward<_ExecutionPolicy>(__exec), __r, __r + __n2,
2591-
[__r, __d_first](_T1* __i, _T1* __j) {
2589+
__par_backend::__parallel_for(__backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __r, __r + __n2,
2590+
[__r, __d_first](_T2* __i, _T2* __j) {
25922591
__brick_move_destroy<__parallel_tag<_IsVector>>{}(
25932592
__i, __j, __d_first + (__i - __r), _IsVector{});
25942593
});
25952594

2596-
if constexpr (!::std::is_trivially_destructible_v<_T1>)
2597-
__par_backend::__parallel_for(__backend_tag{}, ::std::forward<_ExecutionPolicy>(__exec), __r + __n2,
2595+
if constexpr (!std::is_trivially_destructible_v<_T2>)
2596+
__par_backend::__parallel_for(__backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __r + __n2,
25982597
__r + __n1,
2599-
[](_T1* __i, _T1* __j) { __brick_destroy(__i, __j, _IsVector{}); });
2598+
[](_T2* __i, _T2* __j) { __brick_destroy(__i, __j, _IsVector{}); });
26002599

26012600
return __d_first + __n2;
26022601
}

include/oneapi/dpl/pstl/algorithm_ranges_impl.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,39 @@ __pattern_partial_sort_ranges(__serial_tag</*IsVector*/ std::false_type>, _Execu
493493
return std::ranges::partial_sort(std::forward<_R>(__r), __middle, __comp, __proj);
494494
}
495495

496+
//---------------------------------------------------------------------------------------------------------------------
497+
// pattern_partial_sort_copy_ranges
498+
//---------------------------------------------------------------------------------------------------------------------
499+
template <typename _Tag, typename _ExecutionPolicy, typename _R, typename _OutR, typename _Comp, typename _Proj1,
500+
typename _Proj2>
501+
std::ranges::partial_sort_copy_result<std::ranges::borrowed_iterator_t<_R>, std::ranges::borrowed_iterator_t<_OutR>>
502+
__pattern_partial_sort_copy_ranges(_Tag __tag, _ExecutionPolicy&& __exec, _R&& __r, _OutR&& __out_r, _Comp __comp,
503+
_Proj1, _Proj2 __proj2)
504+
{
505+
static_assert(__is_parallel_tag_v<_Tag> || typename _Tag::__is_vector{});
506+
507+
auto [__first, __last] = oneapi::dpl::__ranges::__bounds(__r);
508+
auto [__out_first, __out_last] = oneapi::dpl::__ranges::__bounds(__out_r);
509+
510+
// __pattern_partial_sort_copy sorts after copying, so _Proj1 is not used
511+
auto __out_finish = oneapi::dpl::__internal::__pattern_partial_sort_copy(
512+
__tag, std::forward<_ExecutionPolicy>(__exec), __first, __last, __out_first, __out_last,
513+
oneapi::dpl::__internal::__binary_op<_Comp, _Proj2, _Proj2>{__comp, __proj2, __proj2});
514+
515+
return {__last, __out_finish};
516+
}
517+
518+
template <typename _IsVector, typename _ExecutionPolicy, typename _R, typename _OutR, typename _Comp, typename _Proj1,
519+
typename _Proj2>
520+
std::ranges::partial_sort_copy_result<std::ranges::borrowed_iterator_t<_R>, std::ranges::borrowed_iterator_t<_OutR>>
521+
__pattern_partial_sort_copy_ranges(__serial_tag<_IsVector>, _ExecutionPolicy&& __exec, _R&& __r, _OutR&& __out_r,
522+
_Comp __comp, _Proj1 __proj1, _Proj2 __proj2)
523+
{
524+
// Use the standard implementation for both seq and unseq policies
525+
return std::ranges::partial_sort_copy(std::forward<_R>(__r), std::forward<_OutR>(__out_r), __comp, __proj1,
526+
__proj2);
527+
}
528+
496529
//---------------------------------------------------------------------------------------------------------------------
497530
// pattern_is_heap
498531
//---------------------------------------------------------------------------------------------------------------------

include/oneapi/dpl/pstl/glue_algorithm_ranges_defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct __stable_sort_fn;
5858
struct __sort_leaf;
5959
struct __sort_fn;
6060
struct __partial_sort_fn;
61+
struct __partial_sort_copy_fn;
6162
struct __is_heap_fn;
6263
struct __is_heap_until_fn;
6364
struct __min_element_fn;

include/oneapi/dpl/pstl/glue_algorithm_ranges_impl.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,28 @@ struct __internal::__partial_sort_fn
631631
}; //__partial_sort_fn
632632
inline constexpr __internal::__partial_sort_fn partial_sort;
633633

634+
struct __internal::__partial_sort_copy_fn
635+
{
636+
template <typename _ExecutionPolicy, std::ranges::random_access_range _R, std::ranges::random_access_range _OutR,
637+
typename _Comp = std::ranges::less, typename _Proj1 = std::identity, typename _Proj2 = std::identity>
638+
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<_ExecutionPolicy>> &&
639+
std::ranges::sized_range<_R> && std::ranges::sized_range<_OutR> &&
640+
std::indirectly_copyable<std::ranges::iterator_t<_R>, std::ranges::iterator_t<_OutR>> &&
641+
std::sortable<std::ranges::iterator_t<_OutR>, _Comp, _Proj2> &&
642+
std::indirect_strict_weak_order<_Comp, std::projected<std::ranges::iterator_t<_R>, _Proj1>,
643+
std::projected<std::ranges::iterator_t<_OutR>, _Proj2>>
644+
std::ranges::partial_sort_copy_result<std::ranges::borrowed_iterator_t<_R>, std::ranges::borrowed_iterator_t<_OutR>>
645+
operator()(_ExecutionPolicy&& __exec, _R&& __r, _OutR&& __result, _Comp __comp = {}, _Proj1 __proj1 = {},
646+
_Proj2 __proj2 = {}) const
647+
{
648+
const auto __dispatch_tag = oneapi::dpl::__ranges::__select_backend(__exec);
649+
return oneapi::dpl::__internal::__ranges::__pattern_partial_sort_copy_ranges(
650+
__dispatch_tag, std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r),
651+
std::forward<_OutR>(__result), __comp, __proj1, __proj2);
652+
}
653+
}; //__partial_sort_copy_fn
654+
inline constexpr __internal::__partial_sort_copy_fn partial_sort_copy;
655+
634656
// [is.heap]
635657

636658
struct __internal::__is_heap_fn

include/oneapi/dpl/pstl/hetero/algorithm_impl_hetero.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,7 +1490,7 @@ _OutIterator
14901490
__pattern_partial_sort_copy(__hetero_tag<_BackendTag> __tag, _ExecutionPolicy&& __exec, _InIterator __first,
14911491
_InIterator __last, _OutIterator __out_first, _OutIterator __out_last, _Compare __comp)
14921492
{
1493-
using _ValueType = typename ::std::iterator_traits<_InIterator>::value_type;
1493+
using _ValueType = typename std::iterator_traits<_OutIterator>::value_type;
14941494

14951495
auto __in_size = __last - __first;
14961496
auto __out_size = __out_last - __out_first;
@@ -1519,8 +1519,7 @@ __pattern_partial_sort_copy(__hetero_tag<_BackendTag> __tag, _ExecutionPolicy&&
15191519
// Use regular sort as partial_sort isn't required to be stable.
15201520
//__pattern_sort is a blocking call.
15211521
__pattern_sort(
1522-
__tag,
1523-
__par_backend_hetero::make_wrapped_policy<__partial_sort_1>(::std::forward<_ExecutionPolicy>(__exec)),
1522+
__tag, __par_backend_hetero::make_wrapped_policy<__partial_sort_1>(std::forward<_ExecutionPolicy>(__exec)),
15241523
__out_first, __out_end, __comp);
15251524

15261525
return __out_end;
@@ -1553,7 +1552,7 @@ __pattern_partial_sort_copy(__hetero_tag<_BackendTag> __tag, _ExecutionPolicy&&
15531552

15541553
return __pattern_hetero_walk2<__par_backend_hetero::__deferrable_mode, __par_backend_hetero::access_mode::write,
15551554
/*_IsOutNoInitRequested=*/true>(
1556-
__tag, __par_backend_hetero::make_wrapped_policy<__copy_back>(::std::forward<_ExecutionPolicy>(__exec)),
1555+
__tag, __par_backend_hetero::make_wrapped_policy<__copy_back>(std::forward<_ExecutionPolicy>(__exec)),
15571556
__buf_first, __buf_mid, __out_first, __brick_copy<__hetero_tag<_BackendTag>>{});
15581557

15591558
// The temporary buffer is constructed from a range, therefore it's destructor will not block, therefore

include/oneapi/dpl/pstl/hetero/algorithm_ranges_impl_hetero.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,6 +1520,25 @@ __pattern_partial_sort_ranges(__hetero_tag<_BackendTag> __tag, _ExecutionPolicy&
15201520
}
15211521
#endif //_ONEDPL_CPP20_RANGES_PRESENT
15221522

1523+
#if _ONEDPL_CPP20_RANGES_PRESENT
1524+
template <typename _BackendTag, typename _ExecutionPolicy, typename _R, typename _OutR, typename _Comp, typename _Proj1,
1525+
typename _Proj2>
1526+
std::ranges::partial_sort_copy_result<std::ranges::borrowed_iterator_t<_R>, std::ranges::borrowed_iterator_t<_OutR>>
1527+
__pattern_partial_sort_copy_ranges(__hetero_tag<_BackendTag> __tag, _ExecutionPolicy&& __exec, _R&& __r,
1528+
_OutR&& __out_r, _Comp __comp, _Proj1, _Proj2 __proj2)
1529+
{
1530+
auto [__first1, __last1] = oneapi::dpl::__ranges::__bounds(__r);
1531+
auto [__out_it, __out_end] = oneapi::dpl::__ranges::__bounds(__out_r);
1532+
1533+
// __pattern_partial_sort_copy sorts after copying, so _Proj1 is not used
1534+
auto __out_finish = oneapi::dpl::__internal::__pattern_partial_sort_copy(
1535+
__tag, std::forward<_ExecutionPolicy>(__exec), __first1, __last1, __out_it, __out_end,
1536+
oneapi::dpl::__internal::__binary_op<_Comp, _Proj2, _Proj2>{__comp, __proj2, __proj2});
1537+
1538+
return {__last1, __out_finish};
1539+
}
1540+
#endif //_ONEDPL_CPP20_RANGES_PRESENT
1541+
15231542
//------------------------------------------------------------------------
15241543
// min_element
15251544
//------------------------------------------------------------------------
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// -*- C++ -*-
2+
//===------------------------------------------------------===//
3+
//
4+
// Copyright (C) UXL Foundation Contributors
5+
//
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===------------------------------------------------------===//
9+
10+
#include "std_ranges_test.h"
11+
12+
#if _ENABLE_STD_RANGES_TESTING
13+
namespace dpl_ranges = oneapi::dpl::ranges;
14+
15+
void test_mixed_types()
16+
{
17+
using namespace test_std_ranges;
18+
19+
std::vector<A> r1 = {{1}, {2}, {5}, {0}, {2}, {7}, {3}};
20+
21+
std::vector<int> out_expected = {0, 1, 2, 2, 3};
22+
23+
std::vector<B> out_seq(out_expected.size(), B{0xCD});
24+
std::vector<B> out_par(out_expected.size(), B{0xCD});
25+
std::vector<B> out_unseq(out_expected.size(), B{0xCD});
26+
std::vector<B> out_par_unseq(out_expected.size(), B{0xCD});
27+
28+
dpl_ranges::partial_sort_copy(oneapi::dpl::execution::seq, r1, out_seq, std::ranges::less{}, proj_a, proj_b);
29+
dpl_ranges::partial_sort_copy(oneapi::dpl::execution::par, r1, out_par, std::ranges::less{}, proj_a, proj_b);
30+
dpl_ranges::partial_sort_copy(oneapi::dpl::execution::unseq, r1, out_unseq, std::less{}, proj_a, proj_b);
31+
dpl_ranges::partial_sort_copy(oneapi::dpl::execution::par_unseq, r1, out_par_unseq, std::less{}, proj_a, proj_b);
32+
33+
EXPECT_EQ_RANGES(out_expected, out_seq, "wrong result with seq policy");
34+
EXPECT_EQ_RANGES(out_expected, out_par, "wrong result with par policy");
35+
EXPECT_EQ_RANGES(out_expected, out_unseq, "wrong result with unseq policy");
36+
EXPECT_EQ_RANGES(out_expected, out_par_unseq, "wrong result with par_unseq policy");
37+
#if TEST_DPCPP_BACKEND_PRESENT
38+
auto policy = TestUtils::get_dpcpp_test_policy();
39+
sycl::queue q = policy.queue();
40+
if (q.get_device().has(sycl::aspect::usm_shared_allocations))
41+
{
42+
using r1_alloc_t = sycl::usm_allocator<A, sycl::usm::alloc::shared>;
43+
using out_alloc_t = sycl::usm_allocator<B, sycl::usm::alloc::shared>;
44+
std::vector<A, r1_alloc_t> v1(r1.begin(), r1.end(), r1_alloc_t(q));
45+
std::vector<B, out_alloc_t> out(out_expected.size(), B{0xCD}, out_alloc_t(q));
46+
47+
dpl_ranges::partial_sort_copy(policy, std::ranges::subrange(v1), std::ranges::subrange(out), std::ranges::less{}, proj_a, proj_b);
48+
EXPECT_EQ_RANGES(out_expected, out, "wrong result with device policy");
49+
}
50+
#endif // TEST_DPCPP_BACKEND_PRESENT
51+
}
52+
#endif //_ENABLE_STD_RANGES_TESTING
53+
54+
std::int32_t
55+
main()
56+
{
57+
#if _ENABLE_STD_RANGES_TESTING
58+
using namespace test_std_ranges;
59+
60+
auto checker = TEST_PREPARE_CALLABLE(std::ranges::partial_sort_copy);
61+
62+
test_range_algo<0, int, data_in_out_lim>{big_sz}(dpl_ranges::partial_sort_copy, checker);
63+
test_range_algo<1, int, data_in_out_lim>{}(dpl_ranges::partial_sort_copy, checker, std::greater{}, proj, proj);
64+
test_range_algo<2, P2, data_in_out_lim>{}(dpl_ranges::partial_sort_copy, checker, std::less{}, &P2::proj, &P2::x);
65+
test_range_algo<3, P2, data_in_out_lim>{}(dpl_ranges::partial_sort_copy, checker, std::greater{}, &P2::x, &P2::proj);
66+
67+
// Check if projections are applied to the right sequences and trigger a compile-time error if not
68+
test_mixed_types();
69+
#endif //_ENABLE_STD_RANGES_TESTING
70+
71+
return TestUtils::done(_ENABLE_STD_RANGES_TESTING);
72+
}

test/parallel_api/ranges/std_ranges_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ struct B
161161
{
162162
int b;
163163
operator int() const { return b; }
164+
B& operator=(const A& a) { b = int(a); return *this; }
164165
};
165166

166167
auto proj_a = [](const A& a) { return a.a; };

0 commit comments

Comments
 (0)