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


11

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

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

使用合适的一元运算unary_op

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

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

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

Answers:


4

在这里引用标准

[alg.transform.1]

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
}

独立于UnaryOperation我们

[alg.transform.5]

在一元变换的情况下,结果可能等于第一。

表示明确允许就地操作。

现在

[algorithms.parallel.overloads.2]

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

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


6

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

所以这unary_op很好:

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

但是这个不会:

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

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


1

正如你可以在你引用链接的例子看,修改任何元素并不只是意味着所有类型的修改的内容:

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

Ret fun(const Type &a);

这包括对元素的修改。在最坏的情况下,如果将相同的迭代器用作目标,则修改不应导致迭代器无效,例如,push_backto或erasing vector可能会导致迭代器无效。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.