Skip to content
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

Parallel for_each iterators don't have to be mutable #3056

Merged
merged 2 commits into from
Aug 27, 2022

Conversation

dkolsen-pgi
Copy link
Contributor

@dkolsen-pgi dkolsen-pgi commented Aug 25, 2022

The overloads of for_each and for_each_n that take an execution policy argument do not require that the iterator arguments always be mutable iterators. Whether or not the iterators need to be mutable depends on what the function object does. for_each and for_each_n are unusual, if not unique, in this regard; for all other algorithms the mutability of the iterators is specified by the algorithm. See http://eel.is/c++draft/alg.foreach#7 , though I admit that the wording is not particularly clear about this.

Since the compiler can't reliably figure out whether or not the function object modifies the elements in the sequence, for_each and for_each_n should only check for constant iterators (_REQUIRE_PARALLEL_ITERATOR), not mutable iterators (_REQUIRE_CPP17_MUTABLE_ITERATOR).

This bug was introduced by #2960 . That PR should not have changed for_each or for_each_n.

The overloads of for_each that take an execution policy argument do not
require that the iterator arguments always be mutable iterators.  Whether
or not the iterators need to be mutable depends on what the function
object does.  for_each is unusual, if not unique, in this regard; for all
other algorithms the mutability of the iterators is specified by the
algorithm.  See http://eel.is/c++draft/alg.foreach#7 , though I admit that
the wording is not particularly clear about this.

Since the compiler can't realiably figure out whether or not the function
object modifies the elements in the sequence, for_each should only check
for constant iterators (_REQUIRE_PARALLEL_ITERATOR), not mutable iterators
(_REQUIRE_CPP17_MUTABLE_ITERATOR).

This bug was introduced by microsoft#2960 .
That PR should not have changed for_each.
@dkolsen-pgi dkolsen-pgi requested a review from a team as a code owner August 25, 2022 21:59
@CaseyCarter CaseyCarter added the bug Something isn't working label Aug 25, 2022
@dkolsen-pgi
Copy link
Contributor Author

This is an important fix. My experience with C++ parallel algorithms is that it is very common to use for_each with a counting iterator of some sort, especially when porting existing code that uses for loops. Until now, people have used a hand-crafted counting iterator or counting_iterator from Thrust or Boost, where the iterator could claim to be random access even though it was technically not. As C++20 and C++23 become available, people will switch to using views::iota and views::cartesian_product instead of a non-standard counting iterator. The iterators from those views need to work with parallel for_each, and they will be rejected without this change.

For example:

#include <iostream>
#include <algorithm>
#include <execution>
#include <ranges>

int main()
{
    auto numbers = std::views::iota(0, 100);
    std::cout << "Hello World!\n";
    std::for_each(std::execution::par, numbers.begin(), numbers.end(), [](int i) { std::cout << i << " "; });
    std::cout << "\n";
}

That test program requires this change to compile. (The output will be garbled and in a sort of random order due to the parallelism, but I was just looking for a very quick test case.)

@StephanTLavavej
Copy link
Member

Thanks for this fix! (And noticing/fixing it so quickly, before STL changes stop flowing into VS 2022 17.4 Preview 3 - we should be able to prevent the bug from ever shipping to customers.)

I remember thinking about this question while reviewing the PR (especially because LWG-475 "May the function object passed to for_each modify the elements of the iterated sequence?" was my first submitted issue in 2004 😹), but I didn't think through all the consequences. Your example is compelling.

@dkolsen-pgi
Copy link
Contributor Author

Credit for finding the bug goes to Pili Latiesa. They contacted me as the author of P2408 to ask if views::zip iterators should be usable with for_each and pointed to the _REQUIRE_CPP17_MUTABLE_ITERATOR macro in for_each that would prevent that.

@StephanTLavavej StephanTLavavej self-assigned this Aug 26, 2022
@StephanTLavavej
Copy link
Member

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

Copy link
Member

@CaseyCarter CaseyCarter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the fix, and thanks to Pili for finding this.

@strega-nil-ms
Copy link
Contributor

strega-nil-ms commented Aug 26, 2022

Mmh, I'll note that this isn't a regression, however, this does fix a bug in implementing P2408R5. Thanks for noticing and the quick fix!

@StephanTLavavej StephanTLavavej merged commit 8ca7bd3 into microsoft:main Aug 27, 2022
@StephanTLavavej
Copy link
Member

Thanks again for fixing this, and congratulations on your first microsoft/STL commit! 😸 🎉 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
  翻译: