使用 std::for_each 和 std::views::iota 的并行 for 循环

发布于 2025-01-16 08:11:58 字数 1496 浏览 4 评论 0原文

我想使用 std::views 为基于索引的并行 for 循环设置一个简单的解决方法。

对于按顺序运行,代码如下所示:

int main() {

    //pseudo-random numbers

    random_device rd;
    default_random_engine eng(rd());
    uniform_int_distribution<int> distr(0, 100);

    auto r = ranges::views::iota(0, 10);
    vector<double> v(10, 1);
    for_each(r.begin(), r.end(), [&](int i) {v[i] = distr(eng); });
    for (auto&& i : v) cout << i << " "; cout << endl;
}

这工作正常。我使用的是 std::for_each() 的标准版本,而不是 ranges 命名空间中的版本,因为它们没有执行策略。现在是并行版本。唯一的区别:

for_each(execution::par, r.begin(), r.end(), [&](int i) {v[i] = distr(eng); });

MSVC 给出错误:

error C2338: Parallel algorithms require forward iterators or stronger

我在这里发现了类似的问题:使用范围::view:: iota 并行算法 ,我实现了那里提供的解决方案:

    auto r = views::iota(0) | views::take(10);
    vector<double> v(10, 1);
    auto input_range = ranges::common_view(r);

    for_each(execution::par, ranges::begin(input_range), ranges::end(input_range), [&](int i) {v[i] = distr(eng); });
    for (auto&& i : v) cout << i << " "; cout << endl;

但是,我仍然面临错误

error C2338: Parallel algorithms require forward iterators or stronger.

有人知道这个问题是否有解决方案?

I want to set up an easy workaround for parallelized index-based for-loops using std::views.

For running in sequence the code looks like this:

int main() {

    //pseudo-random numbers

    random_device rd;
    default_random_engine eng(rd());
    uniform_int_distribution<int> distr(0, 100);

    auto r = ranges::views::iota(0, 10);
    vector<double> v(10, 1);
    for_each(r.begin(), r.end(), [&](int i) {v[i] = distr(eng); });
    for (auto&& i : v) cout << i << " "; cout << endl;
}

This works fine. I am using the standard version of std::for_each(), not the one in the ranges namespace, because those don't have the execution policies. Now the parallel version. Only difference:

for_each(execution::par, r.begin(), r.end(), [&](int i) {v[i] = distr(eng); });

MSVC gives an error:

error C2338: Parallel algorithms require forward iterators or stronger

I found a similar issue here: Using ranges::view::iota in parallel algorithms and I implemented the solution offered there:

    auto r = views::iota(0) | views::take(10);
    vector<double> v(10, 1);
    auto input_range = ranges::common_view(r);

    for_each(execution::par, ranges::begin(input_range), ranges::end(input_range), [&](int i) {v[i] = distr(eng); });
    for (auto&& i : v) cout << i << " "; cout << endl;

However, I am still facing the error

error C2338: Parallel algorithms require forward iterators or stronger.

Does someone know whether there is a solution to this issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

岁吢 2025-01-23 08:11:58

views::iota 迭代器的 operator*() 返回类型不是引用类型,而是值类型,这使得它不是 C++17 中的前向迭代器而是输入迭代器。由于 C++17 并行算法需要前向迭代器,因此您无法将其应用于 views::iota

有人知道这个问题是否有解决方案吗?

已经有一篇论文 p2408r4 解决这个问题,因此在标准被采用之前没有简单的解决方案。

The return type of operator*() of views::iota's iterator is not a reference type but a value type, which makes it not a forward iterator but an input iterator in C++17. Since the C++17 parallel algorithm requires forward iterators, you cannot apply it to views::iota.

Does someone know whether there is a solution to this issue?

There is already a paper p2408r4 addressing this issue, so there is no simple solution in the standard until it is adopted.

童话里做英雄 2025-01-23 08:11:58

p2408现已在C++23中采用。在 MSVC 19.34 (VS 17.4) 或更高版本中,如果打开 /std:c++latest ,代码将会编译(/std:c++20 也可以工作) )。 <一href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(文件名:%271%27,fontScale:14,fontUsePx:%270% 27,j:1,lang:c%2B%2B,选择:(endColumn:26,endLineNumber:15,positionColumn:26,positionLineNumber:15,sel ectionStartColumn:26、selectionStartLineNumber:15、startColumn:26、startLineNumber:15)、来源:%27%23include%3Crandom%3E%0A%23include%3Ciostream%3E%0A%23include%3Cranges%3E%0A%23include%3Cexecution% 3E%0A%0Aint+ma in()+%7B%0A%09using+命名空间+std%3B%0A%0A%09random_device+rd%3B%0A%09default_random_engine+eng(rd())%3 B%0A%09uniform_int_distribution%3Cint%3E+distr(0,+100)%3B%0A%0A%09auto+r+%3D+范围::视图::iota(0,+10)% 3B%0A%09向量%3Cdouble%3E+v(10,+1)%3B%0A%09for_each(执行::par,r.begin(),+r.end(),+%5B%26%5D(整数+i) +%7Bv%5Bi%5D+%3D+distr(eng)%3B+%7D)%3B%0A%09for+(自动%26%26+i+:+v)+cout+%3C%3C+i+%3C%3C+% 22+%22%3B+cout+ %3C%3C+endl%3B%0A%7D%27),l:%275%27,n:%270%27,o:%27C%2B%2B+源+%231%27,t:%270% 27)),k:50,l:%274%27,n:% 270%27,o:%27%27,s:0,t:%270%27),(g:!((h:编译器,i:(编译器:vcpp_v19_34_x64,deviceViewOpen:%271%27,过滤器rs:(b:%270%27,二进制:%271%27,binaryObject:%271%27,仅注释:%270%27,demangle:%270%27,指令:%270%2 7、执行:%271%27、英特尔:%270%27、库代码:%270%27、修剪:%271%27)、flagsViewOpen:%271%27、fontScale:14、fon tUsePx:%270%27,j:1,lang:c%2B%2B,库:!(),选项:%27/std:c%2B%2Blatest%27,选择:(endColumn:1,endLineNumber:1 ,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startCo流:1,起始行号:1),来源:1),l:%275%27,n:%270%27,o:%27+x64+msvc+v19.34+(编辑器+%231)%27,t :%270%27 )),k:50,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27)),l:%272%27,n: %270%27,o:%27%27,t:%270%27)),版本:4" rel="nofollow noreferrer">示例

p2408 is adopted in C++23 now. In MSVC 19.34 (VS 17.4) or later, the code will compile if you turn on /std:c++latest (/std:c++20 will also work). EXAMPLE

如梦亦如幻 2025-01-23 08:11:58

也许只是一个小更新。
正如所指出的,该代码不能使用 MSVC 进行编译。
然而,该代码确实使用 g++-11 进行编译,因为 libstd++ 不对迭代器类别进行任何预先检查。
但是,无论执行策略如何,代码始终串行运行。

重要提示:以下只是我的建议,如有错误请指正。
如果您仍然想实现一个简单的基于并行索引的 for 循环,您可以使用 STL 容器而不是视图:

template<typename Policy, typename func>
void parallel_for(Policy p, int first, int last, func f) {

    vector<int> idxs(last - first);
    iota(idxs.begin(), idxs.end(), first);
    for_each(p, begin(idxs), end(idxs), f);
}

我们现在不再使用视图,而是将索引存储在 中矢量。这当然会带来内存和运行时开销,因为必须为容器分配内存并且必须用索引填充容器。
实际运行的函数可能如下所示:

int fib(int i) {
    if (i <= 1)
        return i;
    return fib(i - 1) + fib(i - 2);
}

int main() {

    vector<vector<double>> mat(100, vector<double>(100, 1));

    parallel_for(execution::par, 0, mat.size(), [&mat](int i) {

        for (int j = 0; j < mat[i].size(); ++j) {
            mat[i][j] = fib(40);
        }
        });
}

Maybe just a small update.
As was pointed out, the code does not compile with MSVC.
The code does, however, compile with g++-11 since libstd++ does not do any up-front checking of the iterator category.
However, the code runs always in serial, irrespective of the execution policy.

Important: The following is just my suggestion, please correct me if I am wrong.
If you still want to implement an easy parallelized index-based for loop, you can make use of STL containers rather than views:

template<typename Policy, typename func>
void parallel_for(Policy p, int first, int last, func f) {

    vector<int> idxs(last - first);
    iota(idxs.begin(), idxs.end(), first);
    for_each(p, begin(idxs), end(idxs), f);
}

Instead of having a view, we now just store the indices in a vector<int>. This of course comes with a memory and runtime overhead since memory for the container must be allocated and the container must be filled with the indices.
The function in action could look like this:

int fib(int i) {
    if (i <= 1)
        return i;
    return fib(i - 1) + fib(i - 2);
}

int main() {

    vector<vector<double>> mat(100, vector<double>(100, 1));

    parallel_for(execution::par, 0, mat.size(), [&mat](int i) {

        for (int j = 0; j < mat[i].size(); ++j) {
            mat[i][j] = fib(40);
        }
        });
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文