是否可以在工作后将线程连接到“并行”区域?
Posted
技术标签:
【中文标题】是否可以在工作后将线程连接到“并行”区域?【英文标题】:Is it possible to make thread join to 'parallel for' region after its job? 【发布时间】:2019-06-13 05:05:28 【问题描述】:我有两个作业首先需要同时运行:
1) 可以并行化的for循环
2) 一个线程即可完成的功能
现在,让我描述一下我想做什么。
如果存在 8 个可用线程,
job(1) 和 job(2) 首先必须同时运行,分别使用 7 个线程和 1 个线程。
job(2) 完成后,job(2) 正在使用的线程应该分配给 job(1),它是并行 for 循环。
我使用omp_get_thread_num 来计算每个区域中有多少线程处于活动状态。我希望job(1)
中的线程数在job(2)
完成时增加1。
下面描述了一个可能错误或正确的解决方案:
omp_set_nested(1);
#pragma omp parallel
#pragma omp sections
#pragma omp section // job(2)
// 'printf' is not real job. It is just used for simplicity.
printf("i'm single: %d\n", omp_get_thread_num());
#pragma omp section // job(1)
#pragma omp parallel for schedule(dynamic, 32)
for (int i = 0 ; i < 10000000; ++i)
// 'printf' is not real job. It is just used for simplicity.
printf("%d\n", omp_get_thread_num());
如何才能完成我想要完成的工作?
【问题讨论】:
“我想在job(1)中使用这个线程(job(2))”是什么意思?在您的代码示例中,我没有看到在多个线程中使用任何结果。你只是打印到屏幕上。 你的意思是如果你有 8 个线程可用,7 个线程被分配给 job(1),1 个线程被分配给 job(2)。您希望在 job(2) 完成时为 job(1) 分配 8 个线程? @bradgonesurfing 'printf' 只是为了简单起见。那不是真正的工作。而且,你不明白的那句话是,如果用于'job(2)'的线程完成了它的工作,我希望这个线程在job(2)中并行使用for循环。 @bradgonesurfing 是的,你是对的。但是 job(1) 不能等待 'job(2)' 完成。 好的,那么您应该更新问题以澄清您的意思,因为它并不完全明显。 【参考方案1】:这样的事情呢?
#pragma omp parallel
// note the nowait here so that other threads jump directly to the for loop
#pragma omp single nowait
job2();
#pragma omp for schedule(dynamic, 32)
for (int i = 0 ; i < 10000000; ++i)
job1();
我没有对此进行测试,但是由于 nowait,单线程将仅由一个线程执行,而所有其他线程将直接跳转到 for 循环。 另外我认为它比章节更容易阅读。
【讨论】:
【参考方案2】:另一种表达方式(可能是更好的方式)是使用 OpenMP 任务:
#pragma omp parallel master
#pragma omp task // job(2)
// 'printf' is not real job. It is just used for simplicity.
printf("i'm single: %d\n", omp_get_thread_num());
#pragma omp taskloop // job(1)
for (int i = 0 ; i < 10000000; ++i)
// 'printf' is not real job. It is just used for simplicity.
printf("%d\n", omp_get_thread_num());
如果你的编译器不理解 OpenMP 5.0 版本,那么你必须拆分parallel
和master
:
#pragma omp parallel
#pragma omp master
#pragma omp task // job(2)
// 'printf' is not real job. It is just used for simplicity.
printf("i'm single: %d\n", omp_get_thread_num());
#pragma omp taskloop ]
for (int i = 0 ; i < 10000000; ++i)
// 'printf' is not real job. It is just used for simplicity.
printf("%d\n", omp_get_thread_num());
【讨论】:
我很好奇:您建议master
有什么特别的原因吗?我见过的所有材料都使用single
——尽管也没有特别的原因。我认为在这种情况下它应该无关紧要,但如果可能的话,人们可能会主张通常更喜欢single
。
共有三种模式:master
、single
和 sections
。我使用master
作为OpenMP 5.0 引入parallel master
作为为此目的的组合结构。 parallel single
版本(尚)不存在。也可以使用parallel sections
并且只有一个部分,可以避免使用section
指令。
请注意 omp taskloop
需要 openmp 4.5 或更高版本。
omp 任务可能会导致小型作业的开销,除非作业与任务开销相比非常耗时,否则我会避免它
@amlucas 你对任务开销和动态调度开销有什么想法吗?【参考方案3】:
问题来自同步。在section
结束时,omp 等待所有线程的终止,并且在检查其完成之前不能释放作业 2 上的线程。
解决方案需要抑制与nowait
的同步。
我没有成功抑制与sections
和嵌套并行性的同步。我很少使用嵌套并行区域,但我认为,虽然可以等待部分,但在一个部分内生成新的嵌套并行区域时会出现问题。在无法抑制的并行部分末尾有一个强制同步,它可能会阻止新线程加入池。
我所做的是使用single
线程,没有同步。这样,omp 启动 single
线程并且不等待其完成来启动并行 for
。当线程完成其single
工作时,它会加入线程池以完成对for
的处理。
#include <omp.h>
#include <stdio.h>
int main()
int singlethreadid=-1;
// omp_set_nested(1);
#pragma omp parallel
#pragma omp single nowait // job(2)
// 'printf' is not real job. It is just used for simplicity.
printf("i'm single: %d\n", omp_get_thread_num());
singlethreadid=omp_get_thread_num();
#pragma omp for schedule(dynamic, 32)
for (int i = 0 ; i < 100000; ++i)
// 'printf' is not real job. It is just used for simplicity.
printf("%d\n", omp_get_thread_num());
if (omp_get_thread_num() == singlethreadid)
printf("Hello, I\'m back\n");
【讨论】:
虽然解决方案很好,但为什么建议的section
-approach 不起作用的解释是错误的。 section
的末尾没有同步!问题是只有一个线程甚至进入了section
- 并且嵌套的parallel
区域与外部并行区域的其他线程完全没有关系。
我打错了。我的意思是 sectionS 并且在 sectionS 区域的末尾有一个同步。一个部分是单线程的,同步没有意义。
是的,但在原始示例中只有一个 sections
构造。这不是sections
末尾的同步阻止线程在第二部分提供帮助。事实上,每个section
只有一个线程执行,以及嵌套并行的工作原理。以上是关于是否可以在工作后将线程连接到“并行”区域?的主要内容,如果未能解决你的问题,请参考以下文章
问:如何在没有 USB 或连接到计算机的设备上运行 Flutter 应用程序