如何在工作线程中重用由主线程创建的OMP线程池?

3
在我的c++应用程序开始时,我的主线程使用OMP并行化几个for循环。在第一个并行化的for循环之后,我发现所使用的线程在整个应用程序的持续时间内保持存在,并且在从主线程执行的后续OMP for循环中被重复使用,使用以下命令(在CentOS 7中工作):
for i in $(pgrep myApplication); do ps -mo pid,tid,fname,user,psr -p $i;done

在我的程序中,我从主线程启动了一个boost线程,在这个线程中使用OMP并行化for循环。此时,我看到完全创建了一组新的线程,其中有相当大的开销。

是否可能使boost线程内的OMP并行for循环重用主线程创建的原始OMP线程池?

编辑:一些伪代码:

myFun(data)
{

    // Want to reuse OMP thread pool from main here.
    omp parallel for
    for(int i = 0; i < N; ++i)
    {
       // Work on data
    }

}


main
{

    // Thread pool created here.
    omp parallel for
    for(int i = 0; i < N; ++i)
    {
        // do stuff
    }


    boost::thread myThread(myFun) // Constructor starts thread.

    // Do some serial stuff, no OMP.

    myThread.join();


}

3
OpenMP与其他线程机制的交互不包含在规范中,因此严重依赖于实现。GNU OpenMP运行时在TLS中保留线程池指针,并将其向下(嵌套)传播到团队中。通过pthread_create(或boost::threadstd::thread)启动的线程不会继承该指针,因此会生成一个新的线程池。其他 OpenMP 运行时也可能存在这种情况。 - Hristo Iliev
@HristoIliev 谢谢,这也是我的阅读所得。你知道是否有任何解决办法吗?我可以修改我的应用程序来保持 boost::thread 实例的活性以重复使用其 OMP 线程池,但我希望能够快速修复。 - user1777820
2
我不知道有任何解决方法。此外,从最终用户的角度来看,OpenMP运行时应该是一个黑盒子,以保持范式的可移植性。 - Hristo Iliev
@HristoIliev 这很公平。我不明白为什么他们不会将OMP线程池设为全局,但我相信他们有他们的理由。感谢您确认没有解决方法或我错过了什么。请随意将您的评论复制粘贴到回复中,我会接受它们作为答案。 - user1777820
我已经添加了一个答案,希望能够回答你的问题。 - Hristo Iliev
1个回答

2
OpenMP与其他线程机制的交互在规范中被故意省略,因此严重依赖于实现。GNU OpenMP运行时在TLS中保留了一个指向线程池的指针,并将其传播到(嵌套的)团队中。通过pthread_create(或boost::threadstd::thread)启动的线程不会继承该指针,因此会生成一个新的线程池。这也可能是其他OpenMP运行时的情况。
标准中有一个要求,基本上强制大多数实现采用这种行为方式。它涉及 threadprivate 变量的语义以及它们如何在从同一线程分叉的不同并行区域之间保留其值(OpenMP标准,2.15.2 threadprivate Directive):

非初始线程的线程专用变量中数据的值仅在以下所有条件都满足时保留在两个连续的活动parallel区域之间:

  • 没有一个parallel区域嵌套在另一个显式的并行区域内。
  • 用于执行两个parallel区域的线程数相同。
  • 用于执行两个parallel区域的线程关联策略相同。
  • 在进入两个parallel区域时,封闭任务区域中使用的dyn-var内部控制变量的值为false

如果这些条件都满足,并且在两个区域中引用了线程专用变量,则具有相同线程号的线程将引用该变量的相同副本。

除了性能之外,这可能是使用线程池在OpenMP运行时中的主要原因。
现在,想象一下由两个单独的线程分叉的两个并行区域共享相同的工作线程池。第一个线程分叉了一个并行区域并设置了一些线程专用变量。稍后,同一线程分叉了第二个并行区域,在其中使用了这些线程专用变量。但是,在两个并行区域之间的某个地方,第二个线程分叉了一个并行区域,并利用了来自同一池的工作线程。由于大多数实现将线程专用变量保留在TLS中,因此无法再断言上述语义。一种可能的解决方案是为每个单独的线程向池添加新的工作线程,这与创建新的线程池并没有太大的区别。
我不知道有任何解决方法可以使工作线程池共享。如果可能,它也将不具有可移植性,因此OpenMP的主要优势将会丧失。

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