【问题标题】:Parallel implementations of std algorithms and side effectsstd 算法和副作用的并行实现
【发布时间】:2013-11-21 11:31:41
【问题描述】:

通过std::transform 的标准文档,我注意到在 C++11 之前,函子参数被要求没有副作用,而从 C++11 开始,要求的限制较少 - “op and binary_op 不应使迭代器或子范围无效,或修改范围中的元素”。见

http://en.cppreference.com/w/cpp/algorithm/transform

和标准的第 25.3.4 节。 cppreference.com 上的网页也提到“这些要求的目的是允许 std::transform 的并行或无序实现”。

我不明白这段代码的 sn-p 在 C++11 中是否合法:

std::vector<int> v(/* fill it with something */), v_transformed;
int foo = 0;
std::transform(v.begin(),v.end(),std::back_inserter(v_transformed),[&foo](const int &n) -> int {
    foo += 1;
    return n*2;
});

显然,如果 std::transform 在幕后并行化,我们将有多个并发调用 foo += 1,这将是 UB。但函子本身似乎并没有违反标准中列出的要求。

这个问题可以问其他标准算法(除了我认为std::for_each,它明确指出要按顺序执行迭代)。

我是不是误会了什么?

【问题讨论】:

  • 我刚刚使用 gcc 4.8.2 生成了这个程序集,没有优化和-O2 巨大的差异
  • LWG issue 293 几乎是你的问题.. 但被关闭为 NAD

标签: c++ algorithm c++11


【解决方案1】:

据我了解 C++11 规范,所有标准库函数都必须按顺序执行所有操作,如果它们的效果对用户可见的话。特别是,所有“变异序列操作”都必须按顺序执行。

标准的相关部分是§17.6.5.9/8:

除非另有说明,否则 C++ 标准库函数应仅在当前线程内执行所有操作,前提是这些操作具有对用户可见 (1.10) 的效果。

【讨论】:

  • MWid:谢谢,这真的很有帮助!将答案标记为已接受。
【解决方案2】:

算法的当前定义方式必须按顺序执行,除非实现可以证明并发执行它不会改变语义。我可以想象未来会添加一些明确允许同时执行的算法,但它们将是不同的算法。

【讨论】:

  • 哪里有保证?像 OP 一样,我找不到它,除了 for_each (并且认为有必要为 for_each 指定它的事实一直向我暗示,否则它不是必需的)。
  • 我的初步理解是for_each 有很强的保证以一定的顺序完成事情(我认为这也排除了并发实现),而对于其他算法,迭代可能都出局了有序和并发。但是考虑到对函子的有限要求,并发实现需要能够证明函子没有副作用(例如,也许它可以为诸如std::plus等标准库中的函子的显式特化做到这一点) .
【解决方案3】:

所以 C++11 现在允许 std::transform 被并行化,但这并不能保证您自己的代码将是并行化安全的。现在,是的,我想你必须保护你的数据变量。我可以想象由此产生的许多 MT 错误,如果实现确实平行std::transform

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 2012-01-11
    • 2010-12-26
    相关资源
    最近更新 更多