最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How can I make TBB parallel_for_each work with std::views::zip? - Stack Overflow

programmeradmin3浏览0评论

I'm replacing our own in-house ZipIterator/ZipRange implementation with std::views::zip and am running into a wall when it comes to TBB's parallel_for_each.

With our own ZipRange, it was perfectly capable of modifying one of the elements returned by the ZipRange, but when I replace it with std::views::zip, the functor seems to get a copy, and not a modifiable reference as I would expect, resulting in the parallel_for_each having to effect and leaving the "result" container unmodified.

Since it worked with our (very euhm, involved) in-house ZipIterator/ZipRange, and std::ranges::for_each does what it's supposed to do, but tbb::parallel_for_each leaves the elements unchanged, as demonstrated with 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';
    }
}

How can I make a read-write std::views::zip work with tbb::parallel_for_each? The only relevant reference I could discover is what seems like an admission of a bug in the TBB code, but I would think that has long been fixed (I cannot find the referenced code any more, so maybe this case was lost in some refactoring on their end?):

I'm replacing our own in-house ZipIterator/ZipRange implementation with std::views::zip and am running into a wall when it comes to TBB's parallel_for_each.

With our own ZipRange, it was perfectly capable of modifying one of the elements returned by the ZipRange, but when I replace it with std::views::zip, the functor seems to get a copy, and not a modifiable reference as I would expect, resulting in the parallel_for_each having to effect and leaving the "result" container unmodified.

Since it worked with our (very euhm, involved) in-house ZipIterator/ZipRange, and std::ranges::for_each does what it's supposed to do, but tbb::parallel_for_each leaves the elements unchanged, as demonstrated with 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./z/EvMWGsTP5

How can I make a read-write std::views::zip work with tbb::parallel_for_each? The only relevant reference I could discover is what seems like an admission of a bug in the TBB code, but I would think that has long been fixed (I cannot find the referenced code any more, so maybe this case was lost in some refactoring on their end?): https://community.intel/t5/Intel-oneAPI-Threading-Building/tbb-parallel-for-and-std-for-each/m-p/848767

Share Improve this question asked 4 hours ago rubenvbrubenvb 76.6k37 gold badges199 silver badges347 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

The issue is that std::ranges::zip_view<Rs...>::value_type is std::tuple<std::ranges::range_value_t<Rs>...>. You need to turn it into a range of std::tuple<std::ranges::range_reference_t<Rs>...> instead.

You can do that by zip_transforming with std::forward_as_tuple wrapped in a lambda (as it isn't an addressable function).

See Godbolt

std::ranges::for_each doesn't use the value_type of the passed in range, it does1 f(proj(*it)).

  1. something not observably different to

I'm not sure about this, but TBB’s parallel_for_each doesn’t “see through” the proxy that std::views::zip produces – it ends up passing a copy of the tuple, so modifying that copy doesn’t update your original container. One proven workaround is to “materialize” the underlying references by piping the zip view through std::views::all. For example, change your call to:

tbb::parallel_for_each(
    std::views::zip(ints, doubles, product) | std::views::all,
    multiply
);

This issue is due to how TBB’s algorithm deduces and passes its elements (requiring forward iterator semantics), while std::views::zip returns proxy objects that don’t quite meet that requirement. Piping with std::views::all “unwraps” the proxies into proper references.

发布评论

评论列表(0)

  1. 暂无评论