Unexpected result with read/write zip_view and parallel_for_each
Summary
parallel_for_each gives a different result than std::ranges::for_each when passing a read/write std::ranges::zip_view.
Version
2021.4, 2021.10
Environment
- Hardware: x86_64 and aarch64
- OS name and version: LInux, macOS 15
- Compiler version: Xcode 16.1 Clang and GCC 14.1
Observed Behavior
parallel_for_each "loses" the writable reference to elements in a zip_view, in contrast to std::ranges::for_each, as demonstrated in this program:
#include <ranges>
#include <vector>
#include <iostream>
#include <algorithm>
#include <tbb/parallel_for_each.h>
int main()
{
const std::vector<int> ints{1,2,3};
const std::vector<double> doubles{0.1, 0.2, 0.3};
const auto multiply = [](auto&& intDoubleProduct)
{
auto&& [i, d, product] = intDoubleProduct;
product = i * d;
};
{
std::vector<double> product = {0, 0, 0};
std::ranges::for_each(std::views::zip(ints, doubles, product), multiply);
std::cout << "std::ranges::for_each result: (";
for(auto value : product)
std::cout << value << ", ";
std::cout << '\n';
}
{
std::vector<double> product = {0, 0, 0};
tbb::parallel_for_each(std::views::zip(ints, doubles, product), multiply);
std::cout << "tbb::parallel_for_each result: (";
for(auto value : product)
std::cout << value << ", ";
std::cout << '\n';
}
}
https://godbolt.org/z/EvMWGsTP5
Expected Behavior
parallel_for_each writes to the elements in the zipped vector and matches the outcome of std::ranges::for_each. TBB should be "smarter" about the range it's passed, the core issue seems to be what is described in this StackOverflow answer: https://stackoverflow.com/a/79451943/256138
Note the above example is simple, and the zip_view is passed directly to tbb::parallel_for_each. This case, as described in the comments to that SO answer, can be rewritten to function correctly with a proper wrapper. Unfortunately that inhibits further "range" operations such as std::views::filter, e.g.
const auto is_even = [](const auto& intDoubleProduct) { return std::get<0>(intDoubleProduct) % 2 == 0; };
tbb::parallel_for_each(std::views::zip(ints, doubles, product) | std::views::filter(is_even), multiply);
Which may also change the range/view type that TBB sees in some way (although piping through std::views::all did nothing beneficial to the original issue.
Steps To Reproduce
See code above.
@rubenvb,
Prior to oneTBB 2021.12, parallel_for_each contained an issue while working with C++20 iterators (including iterators of ranges).
It was fixed by https://github.com/uxlfoundation/oneTBB/commit/de0a3a653ba80c1d324e49a1b1faa01fc459d02d
Consider updating the TBB version you use to at least oneTBB 2021.12 to get the fix.
I have tried locally and it fixed the code snippet you provided.