Skip to content

<algorithm>: ranges::minmax should dereference iterators only once - #3366

Merged
Stephan T. Lavavej (StephanTLavavej) merged 17 commits into
microsoft:mainfrom
fsb4000:fix2900
Feb 3, 2023
Merged

<algorithm>: ranges::minmax should dereference iterators only once#3366
Stephan T. Lavavej (StephanTLavavej) merged 17 commits into
microsoft:mainfrom
fsb4000:fix2900

Conversation

@fsb4000

@fsb4000 Igor Zhukov (fsb4000) commented Jan 27, 2023

Copy link
Copy Markdown
Contributor

Fixes #2900

After #2958 the original repro works, but the issue still exists.

I also found that if we have only one element we also have the issue.

@fsb4000
Igor Zhukov (fsb4000) requested a review from a team as a code owner January 27, 2023 16:52
@fsb4000 Igor Zhukov (fsb4000) changed the title fix ranges::minmax <algorithm>: ranges::minmax should dereference iterators only once Jan 27, 2023
Comment thread stl/inc/algorithm Outdated
Igor Zhukov (fsb4000) and others added 2 commits January 28, 2023 00:23
Co-authored-by: Nicole Mazzuca <mazzucan@outlook.com>
Comment thread stl/inc/algorithm Outdated
@StephanTLavavej Stephan T. Lavavej (StephanTLavavej) added bug Something isn't working ranges C++20/23 ranges labels Jan 28, 2023
Comment thread stl/inc/algorithm Outdated
Co-authored-by: Alex Guteniev <gutenev@gmail.com>
Co-authored-by: timsong-cpp <rs2740@gmail.com>
Comment thread stl/inc/algorithm
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread stl/inc/algorithm Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
@StephanTLavavej

This comment was marked as resolved.

Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
@StephanTLavavej

Copy link
Copy Markdown
Member

This new pattern is unusual, but I rate it only 2 squirrels out of 5. (The Standard has an example, WG21-N4928 [dcl.init.aggr]/6, where a default member initializer refers to previous members, that's at least 3 squirrels.) I probably would have commented it, but I don't think it's worth resetting testing again. It does seem to be an improvement compared to the previous version, thanks!

@fsb4000

Copy link
Copy Markdown
Contributor Author

It does seem to be an improvement compared to the previous version, thanks!

Good, I found it there:
https://github.com/llvm/llvm-project/blob/c4e38fadfa2a615467a8c9071f12edc4cdce5eb8/libcxx/include/__algorithm/ranges_minmax.h#L89

libc++ also has the bug, I created: https://reviews.llvm.org/D142864

Comment thread stl/inc/algorithm Outdated
Co-authored-by: Nicole Mazzuca <mazzucan@outlook.com>
@StephanTLavavej

Copy link
Copy Markdown
Member

I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed.

Comment thread stl/inc/algorithm Outdated
Comment thread stl/inc/algorithm Outdated
Comment thread stl/inc/algorithm Outdated
Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Co-authored-by: Casey Carter <cartec69@gmail.com>
@fsb4000

Copy link
Copy Markdown
Contributor Author

Stephan T. Lavavej (@StephanTLavavej)
I pushed some changes after Casey's code review

Comment thread stl/inc/algorithm Outdated
@CaseyCarter

Copy link
Copy Markdown
Contributor

Stephan T. Lavavej (@StephanTLavavej) I pushed some changes after Casey's code review

As did I.

Comment thread tests/std/tests/P0896R4_ranges_alg_minmax/test.cpp Outdated
Comment thread stl/inc/algorithm Outdated
Comment thread stl/inc/algorithm Outdated
@StephanTLavavej

Copy link
Copy Markdown
Member

Everyone's joining the commit party! (I pushed tiny comment changes and removed parameter names for the defaulted function.)

@StephanTLavavej
Stephan T. Lavavej (StephanTLavavej) merged commit 0150312 into microsoft:main Feb 3, 2023
@StephanTLavavej

Copy link
Copy Markdown
Member

Thanks for minimizing bugs while maximizing performance! 📉 😸 📈

Casey Carter (CaseyCarter) added a commit to CaseyCarter/STL that referenced this pull request Feb 3, 2023
The fix for microsoft#2900 in microsoft#3366 did not take advantage of the fact that the minimum and maximum elements of a range are always distinct except when the range has only one element. It couldn't easily do so due to the way `ranges::minmax` and `ranges::minmax_element` share the common backend `ranges::_Minmax_element_unchecked`.

This PR introduces a new backend for `ranges::minmax` (`ranges::_Minmax_fn::_Minmax_fwd_unchecked`) and makes `ranges::_Minmax_element_unchecked` the private member `ranges::_Minmax_element_fn::_Minmax_element_fwd_unchecked`. Since the two are distinct, `_Minmax_fwd_unchecked` can deal in values instead of iterators, and we can unroll the first loop iteration to detect the single-element case naturally. Since no additional branch is needed, we can enable the fix for microsoft#2900 unconditionally.

This refactoring is also probably a minor throughput and perf improvement, since `minmax` no longer needs to instantiate the "old" backend's return type and the new backend is tail-called.

Drive-by: Fully qualify the names of ugly functions called by `ranges::minmax` and `ranges::minmax_element`.
Casey Carter (CaseyCarter) added a commit to CaseyCarter/STL that referenced this pull request Feb 3, 2023
The fix for microsoft#2900 in microsoft#3366 did not take advantage of the fact that the minimum and maximum elements of a range are always distinct except when the range has only one element. It couldn't easily do so due to the way `ranges::minmax` and `ranges::minmax_element` share the common backend `ranges::_Minmax_element_unchecked`.

This PR introduces a new backend for `ranges::minmax` (`ranges::_Minmax_fn::_Minmax_fwd_unchecked`) and makes `ranges::_Minmax_element_unchecked` the private member `ranges::_Minmax_element_fn::_Minmax_element_fwd_unchecked`. Since the two are distinct, `_Minmax_fwd_unchecked` can deal in values instead of iterators, and we can unroll the first loop iteration to detect the single-element case naturally. Since no additional branch is needed, we can enable the fix for microsoft#2900 unconditionally.

Drive-by: Fully qualify the names of ugly functions called by `ranges::minmax` and `ranges::minmax_element`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ranges C++20/23 ranges

Projects

None yet

Development

Successfully merging this pull request may close these issues.

<algorithm>: ranges::minmax initializes minmax_result with the moved value

7 participants