并行化:使用pthreads还是OpenMP?

45

在科学计算中,大多数人在共享内存并行化方面使用OpenMP作为准标准。

除了可读性之外,是否有任何理由选择OpenMP而不是pthreads?后者似乎更基础,我怀疑它可能更快且更容易优化。

6个回答

42

这基本上取决于你想要多大程度地控制你的并行化。如果你只想添加一些#pragma语句并很快地得到代码的并行版本,那么OpenMP就非常适合。如果你想用MIMD编码或复杂的队列做一些真正有趣的事情,你仍然可以使用OpenMP来完成所有这些,但在这种情况下使用线程可能更加简单。就像pthread一样,现在许多不同平台的编译器都支持OpenMP,并具有类似的可移植性优势。

所以你是完全正确的——如果你需要对并行化进行精细调整,使用pthread。如果你希望尽可能少地工作并实现并行化,那么使用OpenMP。

无论你选择哪种方式,祝你好运!


23

另一个原因是:OpenMP 是基于任务的,而 Pthreads 是基于线程的。这意味着 OpenMP 将分配与核心数量相同的线程数。因此,您将获得可扩展的解决方案。如果使用原始线程,则很难完成这项任务。

第二个观点:OpenMP 提供了归约功能:当您需要在线程中计算部分结果并将它们组合起来时,您只需要使用一行代码即可实现。但是,如果使用原始线程,则需要做更多的工作。

考虑一下您的需求并尝试理解:OpenMP 是否足够适合您?您将节省大量时间。


请详细阐述解决方案的可扩展性。可扩展性仅适用于编译时,还是在运行时确定?或者只有通过线程才能实现运行时可伸缩性? - awiebe
1
您可以在编译时或运行时设置创建的线程数。如果选择在运行时设置数量,您可以通过环境变量numthreads设置线程数,以便在任何架构上轻松设置为适当的数量。 - P O'Conbhui
1
这个答案毫无意义。OpenMP就像POSIX线程一样是一个线程模型。在前几个版本中,OpenMP甚至都没有任务。 - Jeff Hammond

8

OpenMP需要支持它的编译器,并使用pragma。这样做的优点是,即使没有OpenMP支持(例如PCC或Clang/LLVM),代码仍将编译。此外,请参阅Charles Leiserson关于DIY多线程的文章

Pthreads是POSIX标准(IEEE POSIX 1003.1c)库,而OpenMP规范则需要在编译器上实现;也就是说,有各种pthread实现(例如OpenBSD rthreads、NPTL),以及许多支持OpenMP的编译器(例如带有-fopenmp标志的GCC、MSVC++2008)。

只有在有多个处理器可用且代码已经针对可用处理器数量进行了优化时,Pthreads才能有效地进行并行化。因此,使用OpenMP编写的代码更易于扩展。您还可以将与使用pthread的代码混合编译。


1
这个答案的最后一段话完全错误。 - Jeff Hammond
实际上只有第一句话是非常错误的。第二句话有些模棱两可,而第三句话在我的看法中看起来还不错。 - xdavidliu

3
您的问题类似于“我应该编写C还是汇编”,其中C代表OpenMP,汇编代表pthreads。
使用pthreads可以实现更好的并行化,更好意味着非常紧密地调整到您的算法和硬件。不过这需要很多工作。
使用pthreads也更容易产生效果不佳的并行化代码。

这假设OpenMP是使用Pthreads实现的。虽然通常是正确的,但并非必需。如果在专用架构上将OpenMP实现到裸机上,则可能比Pthreads更快。 - Jeff Hammond
@Jeff,我并不是在假设这一点,我的回答与实现细节无关。OpenMP和C比pthreads和汇编语言更“高级”,这就是为什么我相信无论C和OpenMP如何实现,我的两个陈述都是正确的。 - steffen
1
似乎你把OpenMP的语法简单性与运行时的语义负担混淆了。你是否比较过POSIX线程规范OpenMP 4规范?特别是,你是否考虑过pthread_create()pragma omp parallel {}所需的内容? - Jeff Hammond

1
除了可读性之外,使用OpenMP而不是pthreads还有其他原因吗?
Mike提到过:
OpenMP在可移植性方面也具有类似的优势,因为许多不同平台的编译器现在都支持它,就像pthreads一样。 Crypto++是跨平台的,意味着它可以在Windows、Linux、OS X和BSD上运行。它在操作可能会很昂贵的地方(如模块指数和模块乘法)以及可以执行并发操作的地方使用OpenMP进行线程支持。
Windows不支持pthreads,但现代Windows编译器支持OpenMP。因此,如果您想要在非Unix系统上实现可移植性,那么OpenMP通常是一个不错的选择。

正如Mike所指出的:

如果你只想添加一些#pragma语句并很快地创建代码的并行版本,那么OpenMP非常好。

以下是Crypto++使用Bernstein在RSA signatures and Rabin-Williams signatures...中描述的Tweaked Roots预计算用于Rabin-Williams签名的一些值的示例:

void InvertibleRWFunction::Precompute(unsigned int /*unused*/)
{
    ModularArithmetic modp(m_p), modq(m_q);

    #pragma omp parallel sections
    {
        #pragma omp section
            m_pre_2_9p = modp.Exponentiate(2, (9 * m_p - 11)/8);
        #pragma omp section
            m_pre_2_3q = modq.Exponentiate(2, (3 * m_q - 5)/8);
        #pragma omp section
            m_pre_q_p = modp.Exponentiate(m_q, m_p - 2);
    }
}

这符合Mike的观察 - 不需要细粒度控制和同步。并行化用于加速执行,同步在源代码中不会产生任何成本。

如果没有可用的OpenMP,则代码将简化为:

m_pre_2_9p = modp.Exponentiate(2, (9 * m_p - 11)/8);
m_pre_2_3q = modq.Exponentiate(2, (3 * m_q - 5)/8);
m_pre_q_p = modp.Exponentiate(m_q, m_p - 2);

0

当您需要并行执行相同任务(即在多个数据上),一种SIMD机器(单指令多数据)时,OpenMP是理想的选择。

当您想要并行执行非常不同的任务时,例如在一个线程中读取数据,在另一个线程中与用户交互时,需要使用Pthreads。

请参阅此页面:

http://berenger.eu/blog/c-cpp-openmp-vs-pthread-openmp-or-posix-thread/


OpenMP一直支持不仅是数据并行。你真的理解OpenMP吗? - Jeff Hammond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接