我可以在并行执行策略中使用std :: transform吗?

问题描述 投票:7回答:3

如果我没记错的话,可以通过使用与输入和输出迭代器相同的范围来使std::transform执行就地。假设我有一些std::vector对象vec,那么我会写

std::transform(vec.cbegin(),vec.cend(),vec.begin(),unary_op)

使用适当的一元运算unary_op

使用C ++ 17标准,我想通过在其中粘贴std::execution::par作为第一个参数来并行执行转换。这会使函数从cppreference article on std::transform中的重载(1)变为(2)。但是,对此超载的注释说:

std::transform [...]不得使任何迭代器(包括最终迭代器)无效,或修改所涉及范围的任何元素。 (自C ++ 11起)

“修饰任何元素”确实意味着我无法就地使用算法,还是在谈论我误解的其他细节?

谢谢。

c++ parallel-processing stl c++17
3个回答
3
投票

在此处引用标准

[alg.transform.1]

op [...]不得使迭代器或子范围无效,也不得修改范围

这禁止您的unary_op修改作为参数给出的值或容器本身。

unary_op

但是,可以的。

auto unary_op = [](auto& value) 
{ 
    value = 10;    // this is bad
    return value;
}

auto unary_op = [&vec](auto const& value) 
{ 
    vec[0] = value;   // also bad
    return value;
}

auto unary_op = [&vec](auto& value) 
{ 
    vec.erase(vec.begin());   // nope 
    return value;
}

独立于auto unary_op = [](auto& value) // const/ref not strictly needed { return value + 10; // totally fine } auto unary_op = [&vec](auto& value) { return value + vec[0]; // ok in sequential but not in parallel execution } 我们有

[alg.transform.5]

如果是一元变换,结果可能等于第一个。

意味着明确允许就地操作。

现在

[algorithms.parallel.overloads.2]

除非另有说明,否则ExecutionPolicy算法重载的语义与其没有时的重载相同。

表示执行策略在算法上没有用户可见的区别。您可以期望该算法产生与未指定执行策略完全相同的结果。


6
投票

我相信这是在谈论一个不同的细节。 UnaryOperation接受序列的一个元素并返回一个值。该值(通过unary_op)存储到目标序列中。

所以此transform很好:

unary_op

但是这个不会:

int times2(int v) { return 2*v; }

但这不是您真正要问的。您想知道是否可以将int times2(int &v) { return v*=2; } unary_op版本用作具有相同源和目标范围的并行算法。我不明白为什么不这样。 transform将源序列的单个元素映射到目标序列的单个元素。但是,如果您的transform并不是真正的一元代码,(即它引用了序列中的其他元素-即使仅读取它们,那么您将进行数据竞争)。


1
投票

如您在引用的链接的示例中所见,修改任何元素没有的确意味着对元素进行了所有类型的修改:

该函数的签名应等同于以下内容:

unary_op

其中包括对元素的修改。在最坏的情况下,如果将相同的迭代器用作目标,则修改不会导致迭代器无效,例如Ret fun(const Type &a); push_back的向量或eras的转换,这可能会导致迭代器无效。

查看您不应该执行的失败示例vector

© www.soinside.com 2019 - 2024. All rights reserved.