使用 std::exclusive_scan 和执行策略 std::execution::par

问题描述

我正在用 prefix product 计算 std::exclusive_scan

#include <algorithm>/
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> input{1,2,3,4,5,6,7,8,9};
    std::exclusive_scan(std::begin(input),std::end(input),std::begin(input),1,std::multiplies<> {});

    std::cout << "exclusive_scan: "
    for (auto const product : input) {
        std::cout << product << " ";
    }
    std::cout << '\n';
    return 0;
}

输出符合预期 exclusive_scan: 1 1 2 6 24 120 720 5040 40320

现在我想将它与 std::execution::par 并行化并更改代码

#include <algorithm>
#include <execution>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> input{1,9};
    std::exclusive_scan(std::execution::par,std::multiplies<> {});

    std::cout << "exclusive_scan: "
    for (auto const product : input) {
        std::cout << product << " ";
    }
    std::cout << '\n';
    return 0;
}

但现在输出exclusive_scan: 1 1 1 1 1 1 1 1 1

我正在使用 gcc 10.2.0 并链接 -ltbb

为什么我不能将 std::exclusive_scanstd::execution::par 一起使用?
我的实现方式有问题吗?
需要进行哪些更改?

解决方法

当您将 exclusive_scan 与默认执行策略一起使用时,如:

exclusive_scan(input.begin(),input.end(),output.begin(),initial_value,operate)

会发生什么:

initial_value_temp = operate(input[0],initial_value);
output[0] = initial_value;
initial_value = initial_value_temp

initial_value_temp = operate(input[1],initial_value);
output[1] = initial_value;
initial_value = initial_value_temp

     ⋮

initial_value_temp = operate(input[last],initial_value);
output[last] = initial_value;
initial_value = initial_value_temp

但是,当您尝试对其应用 execution::par 时,出于某种原因,可能是为了更好的并行化,它会像这样工作:

output[0] = initial_value;
initial_value = operate(input[0],initial_value);

output[1] = initial_value;
initial_value = operate(input[1],initial_value);

     ⋮

output[last] = initial_value;
initial_value = operate(input[last],initial_value);

如果 inputoutput 不同,它们的工作方式应该完全相同。但是,当它们相同时,流程就会中断,这正是您想要做的。

要修复它,最简单的方法是创建一个输出容器:

std::vector<int> input{1,2,3,4,5,6,7,8,9};

std::vector<int> output;
output.reserve(input.size());

std::exclusive_scan(std::execution::par,std::begin(input),std::end(input),std::back_inserter(output),1,std::multiplies{});

  • 注意 execution::par 实际上会尝试将整个任务分配到不同的线程中,然后再合并它们。有很多关于它应该如何工作的文章。但是,这里的要点是您在生成输出时尝试编辑输入。而且只有 9 个数字,它从来没有为我创建任何并行线程(创建一个可能会慢得多)。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...