我可以安全地使用OpenMP与C + + 11吗?

OpenMP标准只考虑C ++ 98(ISO / IEC 14882:1998)。 这意味着在C ++ 03甚至C ++ 11下没有标准的OpenMP支持。 因此,任何使用C ++> 98和OpenMP的程序都会在标准之外运行,这意味着即使它在某些条件下工作,也不太可能是便携式的,但绝对不能保证。

C ++ 11拥有自己的multithreading支持,情况更糟糕,很可能在某些实现中会与OpenMP冲突。

那么,使用C ++ 03和C ++ 11的OpenMP有多安全?

在一个相同的程序中是否可以安全地使用C ++ 11multithreading和OpenMP,但是不会交织它们(即,在任何代码中没有OpenMP语句传递给C ++ 11并发特性,线程中没有C ++ 11并发由OpenMP产生)?

我特别感兴趣的是我首先使用OpenMP调用一些代码,然后在相同的数据结构上使用C ++ 11并发的其他代码。

Walter,我相信我不仅在其他的讨论中告诉过你现在的状况,而且直接从源代码(也就是我的OpenMP语言委员会成员的同事)向你提供了信息。

OpenMP被devise为FORTRAN和C的轻量级数据并行加法,后来扩展为C ++习惯用法(例如随机访问迭代器上的并行循环),并引入显式任务来执行并行任务。 它意味着尽可能在所有三种语言平台上移植 ,并提供基本相同的function。 其执行模型非常简单 – 单线程应用程序在并行区域中分派线程队,在内部运行一些计算任务,然后将这些队join到串行执行中。 如果启用了嵌套并行,则来自并行团队的每个线程都可以稍后分派自己的团队。

由于OpenMP的主要用途是高性能计算(毕竟,它的指令和执行模式是从高性能Fortran中借用的),所以任何OpenMP实现的主要目标都是效率,而不是与其他线程范例的互操作性。 在某些平台上,只有在OpenMP运行时间是控制进程线程的唯一一个时,才能实现高效的实现。 此外,OpenMP的某些方面可能无法与其他线程结构一起使用,例如在分叉两个或多个并行并行区域时由OMP_THREAD_LIMIT设置的线程数量限制。

由于OpenMP标准本身并不严格禁止使用其他线程范例,但既不标准化与此类的互操作性,也支持这些function取决于实现者。 这意味着某些实现可能会提供安全的并发执行的顶级OpenMP区域,有些可能不会。 x86实现者承诺支持它,可能是因为他们中的大多数人也是其他执行模型的支持者(例如Intel与Cilk和TBB,GCC与C ++ 11等),x86通常被认为是“实验”平台其他供应商通常更保守)。

对于它所使用的C ++特性(SC12草案在这里 ),OpenMP 4.0也不比ISO / IEC 14882:1998更进一步。 现在这个标准包含了可移植的线程亲和性,这与其他线程模式无法很好地协作,这可能会提供自己的与OpenMP冲突的绑定机制。 OpenMP语言再一次针对HPC(数据和任务并行的科学和工程应用)。 C ++ 11构造是针对通用计算应用程序的。 如果你想要C ++ 11并发的东西,那么只能使用C ++ 11,或者如果你真的需要将它与OpenMP混合使用,那么如果你想保持可移植性,就要坚持使用C ++ 98的语言特性子集。

我特别感兴趣的是我首先使用OpenMP调用一些代码,然后在相同的数据结构上使用C ++ 11并发的其他代码。

没有什么明显的原因是你不可能做到的,但是取决于你的OpenMP编译器和运行时。 有免费和商业的图书馆使用OpenMP进行并行执行(例如MKL),但总是有可能与multithreading代码不兼容的警告(尽pipe有时深藏在他们的用户手册中),这些代码提供关于什么和什么时候可能的信息。 与往常一样,这超出了OpenMP标准的范围,因此也是YMMV的范围。

实际上我对高性能计算很感兴趣,但OpenMP(目前)并没有达到我的目的:它不够灵活(我的algorithm不是基于循环的)

也许你真的在找TBB ? 它提供了对基于循环和任务的并行机制的支持,以及标准C ++中的各种并行数据结构,并且是便携式和开源的。

(完全免责声明:我为英特尔公司工作,他们与TBB密切相关,但实际上我并不是 TBB上工作而是在OpenMP上工作:-); 我当然不是说英特尔!)。

像Jim Cownie一样,我也是英特尔员工。 我同意他的看法,Intel线程构build模块(Intel Threading Building Blocks,Intel TBB)可能是一个不错的select,因为它具有OpenMP等循环级别的并行性,而且还具有其他并行algorithm,并行容器和低级function。 TBB试图跟上当前的C ++标准。

为了阐明沃尔特,英特尔TBB包括一个parallel_reducealgorithm以及对primefaces和互斥体的高级支持。

您可以在http://software.intel.com/sites/products/documentation/doclib/tbb_sa/help/tbb_userguide/title.htm上find“英特尔®线程构build模块的用户指南”。“用户指南”概述了“图书馆。;

OpenMP通常(我知道没有例外)在Pthread之上实现,所以你可以通过考虑C ++ 11并发与Pthread代码如何互操作来推理一些互操作性问题。

我不知道是否由于使用multithreading模型而导致的超额订阅对您来说是一个问题,但这对于OpenMP肯定是一个问题。 有一个build议 ,在OpenMP 5中解决这个问题。在此之前,你如何解决这个问题是实现定义的。 它们是重锤,但可以使用OMP_WAIT_POLICY (OpenMP 4.5+), KMP_BLOCKTIME (Intel和LLVM)和GOMP_SPINCOUNT (GCC)来解决此问题。 我相信其他的实现有类似的东西。

互操作性是一个真正关心的问题是内存模型,即primefaces操作的行为。 这目前尚未定义,但您仍然可以推论。 例如,如果在OpenMP并行性中使用C ++ 11primefaces,则应该没问题,但是您有责任从OpenMP线程正确使用C ++ 11primefaces。

混合OpenMPprimefaces和C ++ 11primefaces是一个坏主意。 我们(OpenMP语言委员会工作组负责研究OpenMP 5基本语言支持)目前正试图将其解决。 就我个人而言,我认为C ++ 11primefaces在各方面都比OpenMPprimefaces更好,所以我build议你为#pragma omp atomic使用C ++ 11(或C11或__atomic ),并为Fortran程序员留下#pragma omp atomic

下面是使用OpenMP线程使用C ++ 11primefaces的示例代码 。 它在我testing过的任何地方都能正常工作。

充分披露:像Jim和Mike一样,我为Intel工作:-)

 #if defined(__cplusplus) && (__cplusplus >= 201103L) #include <iostream> #include <iomanip> #include <atomic> #include <chrono> #ifdef _OPENMP # include <omp.h> #else # error No OpenMP support! #endif #ifdef SEQUENTIAL_CONSISTENCY auto load_model = std::memory_order_seq_cst; auto store_model = std::memory_order_seq_cst; #else auto load_model = std::memory_order_acquire; auto store_model = std::memory_order_release; #endif int main(int argc, char * argv[]) { int nt = omp_get_max_threads(); #if 1 if (nt != 2) omp_set_num_threads(2); #else if (nt < 2) omp_set_num_threads(2); if (nt % 2 != 0) omp_set_num_threads(nt-1); #endif int iterations = (argc>1) ? atoi(argv[1]) : 1000000; std::cout << "thread ping-pong benchmark\n"; std::cout << "num threads = " << omp_get_max_threads() << "\n"; std::cout << "iterations = " << iterations << "\n"; #ifdef SEQUENTIAL_CONSISTENCY std::cout << "memory model = " << "seq_cst"; #else std::cout << "memory model = " << "acq-rel"; #endif std::cout << std::endl; std::atomic<int> left_ready = {-1}; std::atomic<int> right_ready = {-1}; int left_payload = 0; int right_payload = 0; #pragma omp parallel { int me = omp_get_thread_num(); /// 0=left 1=right bool parity = (me % 2 == 0); int junk = 0; /// START TIME #pragma omp barrier std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now(); for (int i=0; i<iterations; ++i) { if (parity) { /// send to left left_payload = i; left_ready.store(i, store_model); /// recv from right while (i != right_ready.load(load_model)); //std::cout << i << ": left received " << right_payload << std::endl; junk += right_payload; } else { /// recv from left while (i != left_ready.load(load_model)); //std::cout << i << ": right received " << left_payload << std::endl; junk += left_payload; ///send to right right_payload = i; right_ready.store(i, store_model); } } /// STOP TIME #pragma omp barrier std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); /// PRINT TIME std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0); #pragma omp critical { std::cout << "total time elapsed = " << dt.count() << "\n"; std::cout << "time per iteration = " << dt.count()/iterations << "\n"; std::cout << junk << std::endl; } } return 0; } #else // C++11 #error You need C++11 for this test! #endif // C++11