-
Notifications
You must be signed in to change notification settings - Fork 372
[STF] Move unstable_unique from STF to generic cudax utility #8190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2cedca7
ce6a160
cb65d4f
fa0e63b
093cd95
c8386a9
769b0fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||
| //===----------------------------------------------------------------------===// | ||||||||||
| // | ||||||||||
| // Part of CUDA Experimental in CUDA C++ Core Libraries, | ||||||||||
| // 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 | ||||||||||
| // SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. | ||||||||||
| // | ||||||||||
| //===----------------------------------------------------------------------===// | ||||||||||
|
|
||||||||||
| #ifndef _CUDAX__UTILITY_UNSTABLE_UNIQUE | ||||||||||
| #define _CUDAX__UTILITY_UNSTABLE_UNIQUE | ||||||||||
|
|
||||||||||
| #include <cuda/std/detail/__config> | ||||||||||
|
|
||||||||||
| #if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) | ||||||||||
| # pragma GCC system_header | ||||||||||
| #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) | ||||||||||
| # pragma clang system_header | ||||||||||
| #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) | ||||||||||
| # pragma system_header | ||||||||||
| #endif // no system header | ||||||||||
|
|
||||||||||
| #include <cuda/std/__cccl/assert.h> | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
all |
||||||||||
| #include <cuda/std/__iterator/next.h> | ||||||||||
| #include <cuda/std/__iterator/prev.h> | ||||||||||
| #include <cuda/std/__utility/move.h> | ||||||||||
|
|
||||||||||
| namespace cuda::experimental | ||||||||||
| { | ||||||||||
| //! @brief Removes duplicates from a sorted range using a custom predicate. | ||||||||||
| //! | ||||||||||
| //! Operates from both sides of the range, moving elements from the right-hand | ||||||||||
| //! side to the left to eliminate duplicates. The relative order of elements is | ||||||||||
| //! not preserved. | ||||||||||
| //! | ||||||||||
| //! @tparam _Iterator The type of the iterator. | ||||||||||
| //! @tparam _BinaryPredicate The type of the predicate. | ||||||||||
| //! | ||||||||||
| //! @param[in] __first Iterator to the beginning of the range. | ||||||||||
| //! @param[in] __last Iterator past the end of the range. | ||||||||||
| //! @param[in] __pred The predicate used to compare adjacent elements. | ||||||||||
| //! | ||||||||||
| //! @return Iterator to the new end of the range after duplicates have been removed. | ||||||||||
| template <class _Iterator, class _BinaryPredicate> | ||||||||||
| _CCCL_HOST_API _Iterator unstable_unique(_Iterator __first, _Iterator __last, _BinaryPredicate __pred) | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like this algorithm requires random access iterators. Consider Note if constexpr (/* is_random_access_iterator */)then you could at least relax to bidirectional.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually it should work with bidir. Though improving the algo wasn't the primary purpose of this PR, we appreciate the review and take the opportunity to polish the code a lil. |
||||||||||
| { | ||||||||||
| if (__first == __last || ::cuda::std::next(__first) == __last) | ||||||||||
| { | ||||||||||
| return __last; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| ++__first; | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be moved into the first clause of the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, though we lose the comment (which was lost already lol). |
||||||||||
| bool __first_is_known_duplicate = false; | ||||||||||
|
|
||||||||||
| for (; __first < __last; ++__first) | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
to make it work with bidir |
||||||||||
| { | ||||||||||
| if (!__first_is_known_duplicate) | ||||||||||
| { | ||||||||||
| if (!__pred(*__first, *::cuda::std::prev(__first))) | ||||||||||
| { | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| _CCCL_ASSERT(__first < __last, "unstable_unique: iterator out of range"); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
also for bidir |
||||||||||
| for (--__last;; --__last) | ||||||||||
| { | ||||||||||
| if (__first == __last) | ||||||||||
| { | ||||||||||
| return __first; | ||||||||||
| } | ||||||||||
| _CCCL_ASSERT(__first < __last, "unstable_unique: iterator out of range"); | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| if (!__pred(*__last, *::cuda::std::prev(__last))) | ||||||||||
| { | ||||||||||
| break; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| _CCCL_ASSERT(!__pred(*__first, *__last), "unstable_unique: unexpected duplicate"); | ||||||||||
| __first_is_known_duplicate = __pred(*__first, *::cuda::std::next(__first)); | ||||||||||
| *__first = ::cuda::std::move(*__last); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return __first; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| //! @brief Removes duplicates from a sorted range using `operator==`. | ||||||||||
| //! | ||||||||||
| //! Equivalent to calling the predicate overload with `operator==`. | ||||||||||
| //! | ||||||||||
| //! @tparam _Iterator The type of the iterator. | ||||||||||
| //! | ||||||||||
| //! @param[in] __first Iterator to the beginning of the range. | ||||||||||
| //! @param[in] __last Iterator past the end of the range. | ||||||||||
| //! | ||||||||||
| //! @return Iterator to the new end of the range after duplicates have been removed. | ||||||||||
| template <class _Iterator> | ||||||||||
| _CCCL_HOST_API _Iterator unstable_unique(_Iterator __first, _Iterator __last) | ||||||||||
| { | ||||||||||
| return ::cuda::experimental::unstable_unique(__first, __last, [](const auto& __a, const auto& __b) { | ||||||||||
| return __a == __b; | ||||||||||
| }); | ||||||||||
|
Comment on lines
+99
to
+101
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer
Comment on lines
+99
to
+101
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nice, thx @Jacobfaib |
||||||||||
| } | ||||||||||
| } // namespace cuda::experimental | ||||||||||
|
|
||||||||||
| #endif // _CUDAX__UTILITY_UNSTABLE_UNIQUE | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Part of CUDA Experimental in CUDA C++ Core Libraries, | ||
| // 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 | ||
| // SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include <cuda/experimental/__utility/unstable_unique.cuh> | ||
|
|
||
| #include <vector> | ||
|
|
||
| #include <catch2/catch_test_macros.hpp> | ||
|
|
||
| namespace cudax = cuda::experimental; | ||
|
|
||
| TEST_CASE("unstable_unique empty range", "[utility]") | ||
| { | ||
| std::vector<int> v; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding some tests with other iterator categories. For example |
||
| auto new_end = cudax::unstable_unique(v.begin(), v.end()); | ||
| REQUIRE(v.end() == new_end); | ||
| } | ||
|
|
||
| TEST_CASE("unstable_unique no duplicates", "[utility]") | ||
| { | ||
| std::vector<int> v = {1, 2, 3, 4, 5}; | ||
| auto new_end = cudax::unstable_unique(v.begin(), v.end()); | ||
| REQUIRE(v.end() == new_end); | ||
| REQUIRE(std::vector<int>({1, 2, 3, 4, 5}) == v); | ||
| } | ||
|
|
||
| TEST_CASE("unstable_unique leading duplicates", "[utility]") | ||
| { | ||
| std::vector<int> v = {1, 1, 2, 3, 4, 5}; | ||
| auto new_end = cudax::unstable_unique(v.begin(), v.end()); | ||
| REQUIRE(v.begin() + 5 == new_end); | ||
| REQUIRE(std::vector<int>({1, 5, 2, 3, 4, 5}) == v); | ||
| } | ||
|
|
||
| TEST_CASE("unstable_unique interleaved duplicates", "[utility]") | ||
| { | ||
| std::vector<int> v = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5}; | ||
| auto new_end = cudax::unstable_unique(v.begin(), v.end()); | ||
| REQUIRE(v.begin() + 5 == new_end); | ||
| REQUIRE(std::vector<int>({1, 5, 2, 4, 3, 3, 4, 4, 5, 5}) == v); | ||
| } | ||
|
|
||
| TEST_CASE("unstable_unique all same", "[utility]") | ||
| { | ||
| std::vector<int> v = {1, 1, 1, 1, 1}; | ||
| auto new_end = cudax::unstable_unique(v.begin(), v.end()); | ||
| REQUIRE(1 + v.begin() == new_end); | ||
| REQUIRE(std::vector<int>({1, 1, 1, 1, 1}) == v); | ||
| } | ||
|
|
||
| TEST_CASE("unstable_unique trailing unique", "[utility]") | ||
| { | ||
| std::vector<int> v = {1, 1, 1, 1, 1, 2}; | ||
| auto new_end = cudax::unstable_unique(v.begin(), v.end()); | ||
| REQUIRE(v.begin() + 2 == new_end); | ||
| REQUIRE(std::vector<int>({1, 2, 1, 1, 1, 2}) == v); | ||
| } | ||
|
|
||
| TEST_CASE("unstable_unique with custom predicate", "[utility]") | ||
| { | ||
| std::vector<int> v = {1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 5}; | ||
| auto new_end = cudax::unstable_unique(v.begin(), v.end(), [](int a, int b) { | ||
| return a == b; | ||
| }); | ||
| REQUIRE(v.begin() + 5 == new_end); | ||
| REQUIRE(std::vector<int>{1, 5, 4, 3, 2, 1, 1, 1, 1, 2, 2, 2, 3, 4, 5} == v); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is an algorithm, I think it should go to
<cuda/experimental/algorithm>