混合 C++11 原子和 OpenMP

Posted

技术标签:

【中文标题】混合 C++11 原子和 OpenMP【英文标题】:Mixing C++11 atomics and OpenMP 【发布时间】:2017-05-09 14:57:13 【问题描述】:

OpenMP 有自己对原子访问的支持,但是,至少有两个原因更喜欢 C++11 原子:它们更灵活,并且它们是标准的一部分。另一方面,OpenMP 比 C++11 线程库更强大。

该标准在两个不同的章节中指定了原子操作库线程支持库。这让我相信原子访问的组件与使用的线程库有点正交。我真的可以将 C++11 atomics 和 OpenMP 结合起来吗?


Stack Overflow上有一个很similar question;但是,由于它的答案没有回答实际问题,因此三年来基本上没有答案。

【问题讨论】:

为什么你不能?只是不要尝试获取 C++ 互斥体并使用 OpenMP 等待它。 @brianbeuning 好吧,我不确定,这就是我问的原因。链接问题有一条评论推测我们“可能会遇到问题”。我在网上找不到任何可靠的答案,这就是我再次提出这个问题的原因。 这是实现定义的行为,可能因编译器而异。但是,还有一个更“实用”的答案。在大多数情况下,如果标准库和 OpenMP 运行时来自同一编译器供应商,那么您很可能会没事。例如,将 GCC 与 libstdc++ 和 libgomp 一起使用,与 libc++ 和 LLVM (Intel) 运行时一起使用。使用没有自己的标准库的编译器时可能会出现问题,例如 Linux 上带有 libstdc++ 的 Intel C++ 或 macOS 上带有 libc++ 的编译器。我在这种情况下遇到过问题,但很少见。 也就是说,在大多数情况下,您应该期望它们按预期工作。但只是更仔细地测试它。标准库线程和 OpenMP 等线程工具更多地是特定于操作系统的问题,而原子则更多地与硬件相关。有可能工具链的一部分,比如编译器中的 OpenMP 滥用了操作系统特性,而另一部分,标准库对操作系统做出了不同的假设,最终它们不能很好地发挥作用。任何软件都可能有错误。但如果不同的部件来自同一个供应商,它可能更有可能得到更彻底的测试 “这让我相信原子访问的组件与所使用的线程库有点正交” - 根据我的理解,它们并不是正交的.相反,它们是不同的原语,在略微不同的级别上运行以解决不同的问题。如果我必须根据我的理解来描述它们,那么我会说它们是相互恭维的。另见C++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?。 【参考方案1】:

更新:

OpenMP 5.0 定义了与 C++11 及进一步的交互。其中,它表示使用以下功能可能会导致未指定的行为:

数据依赖排序:原子和内存模型 对标准库的补充 C++11 库

很明显,混合 C++11 原子和 OpenMP 5.0 将导致未指定的行为。至少标准本身承诺“OpenMP 规范的未来版本有望解决 [这些] 特性”。

旧讨论:

有趣的是,OpenMP 4.5 标准 (2.13.6) 对 C++11 原子的引用相当模糊,或者更具体的是 std::memory_order

意图是,当类似的操作存在于 C++11 或 C11,顺序一致的原子构造具有相同的语义 作为 C++11/C11 中的 memory_order_seq_cst 原子操作。同样,一个 非顺序一致的原子构造具有相同的语义 C++11/C11 中的 memory_order_relaxed 原子操作。

不幸的是,这只是一个音符,没有任何东西可以定义他们在一起演奏得很好。特别是,即使是最新的 OpenMP 5.0 预览版仍然将 C++98 称为 C++ 的唯一规范参考。所以从技术上讲,OpenMP 甚至不支持 C++11 本身。

除此之外,它可能在实践中的大部分时间都有效。我同意与 C++11 线程相比,如果与 OpenMP 一起使用,使用 std::atomic 出现问题的可能性更小。但如果有什么麻烦,可能就不是那么明显了。最坏的情况是不能以原子方式运行的原子,即使我很难想象可能发生这种情况的现实场景。归根结底,这可能不值得,最安全的做法是坚持使用纯 OpenMP 或纯 C++11 线程/原子。

也许 Hristo 对此有话要说,同时查看this answer 进行更一般的讨论。虽然有点过时,但恐怕它仍然存在。

【讨论】:

我曾经问过一位 OpenMP ARB 的成员,非官方的回答是 OpenMP 和任何其他线程范例之间的互操作性可能永远不会进入规范,但大多数正如 Yan Zhou 在 cmets 中总结的那样,供应商无论如何都会做正确的事情(tm)。换句话说,这样的代码大部分都可以工作,但永远无法移植。【参考方案2】:

OpenMP 4.5 目前未指定此项。在实践中,您可以在大多数编译器中将 C++11 原子操作与 OpenMP 线程一起使用,但没有正式的保证它会起作用。

由于未指定的行为,GCC 直到最近才支持 C11 原子(在语义上与 C++11 原子几乎相同)和 OpenMP 线程。详情请见https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65467。

OpenMP 5.0 已尝试解决此问题。规范语言参考已更新为 C11 和 C++11。但是,这些中的原子和内存模型是“不支持的”,这意味着实现定义。我希望 OpenMP 5.0 能说得更多,但定义 OpenMP 和 ISO 语言原子的交互非常困难。

【讨论】:

现在 OpenMP 5.0 版公众意见草案已经发布,您可能需要更新您的答案。 @user1494080 感谢您的提醒。下次我在办公桌前时会尝试解决这个问题。

以上是关于混合 C++11 原子和 OpenMP的主要内容,如果未能解决你的问题,请参考以下文章

混合原子和非原子变量和缓存

Helgrind (Valgrind) 和 OpenMP (C):避免误报?

c++11 进程间原子和互斥锁

原子操作 vs 非原子操作

c++11 多读取器/多写入器队列使用原子用于对象状态和永久递增索引

C++11多线程 原子操作概念及范例