如何使用OpenMP制作生产者-消费者代码?

时间:2019-02-05 21:26:05

标签: c multithreading parallel-processing task openmp

我试图在OpenMP C代码中使单个线程(主线程)连续执行单个操作(即生成任务),而其他线程(从属)等待主线程生成的任务。从事于。是否可以使主线程仅在其特定任务上工作,而无需安排其他任务?

这样做的目的是拥有一个线程,一直与外部设备进行通信,并产生要由其他线程执行的任务。

以下是我正在尝试的示例:

#pragma omp parallel
{
    #pragma omp master
    {
        printf("MASTER START\n"); fflush(stdout);
        for(int i = 0; i < 1000; ++i) {
            #pragma omp task
            {
                printf("[Thread %d] working on task %d\n", omp_get_thread_num(), i);
                fflush(stdout);
            }
        }
        printf("MASTER END\n"); fflush(stdout);
    }
}

用两个线程执行上面的代码,我得到这样的东西:

MASTER START
...
[Thread 1] working on task 998
[Thread 0] working on task 999
...
MASTER END
[Thread 1] working on task 694
[Thread 0] working on task 696
...

这清楚地表明主线程在完成其工作之前正在执行它创建的任务。那就是我要避免的。主线程应该专门在工作中工作,然后再帮助其他人完成任务。

注意:我知道OpenMP规范说任务可以在创建时(由创建者)直接执行,而不是进入队列。因此,也许我不能使用任务指令来获得所需的结果?使用OpenMP还有其他方法吗?

编辑

只是为了阐明为什么我希望主线程不执行这些任务:在我的应用程序中,主线程负责将任务发送到GPU并接收结果。在GPU上尚未完成的一些工作是将它们委派给CPU线程(目前为omp任务形式)。如果主线程开始处理CPU任务,则它们将无法与GPU通信,GPU将处于空闲状态。

我正在使用的编译器是nvcc 10.0.130和gcc 8.2.1

1 个答案:

答案 0 :(得分:2)

答案比实际需要的时间更长,请务必阅读所有内容。我还想包括理论部分,不幸的是,这些部分实际上并不重要。

通常,OpenMP为实现提供了很多执行自由。这可以允许实现的优化-我鼓励您执行相同的操作:允许实现-编译器和库-尽其所能,同时提供尽可能多的信息。同时,使用了解OpenMP的工具来端到端衡量性能。尝试了解实际发生的情况,而不是仅凭 直观地了解最佳选择。

OpenMP 4.5提供了一种告诉编译器所需内容的方法-任务优先级。即

#pragma omp single
{
    #pragma omp task priority(10)
    {
        printf("MASTER START\n");
        fflush(stdout);
        for (int i = 0; i < 1000; ++i)
        {
            #pragma omp task priority(0)
            {
                    printf("[Thread %d] working on task %d\n", omp_get_thread_num(), i);

按照标准,建议先执行数值优先级较高的任务,再执行优先级较低的任务。这只是提示,实现可以随意忽略。

标准提供的自由度也允许lazy impementations。例如,libgomp(gcc的OpenMP库)具有a hard-coded logic,可以在每个线程中有64个以上的排队任务时立即执行所有产生的任务。我认为优先级与决定是否推迟任务(仅对于排队的任务)无关紧要。

您可以使用以下类似内容进行观察:

int task_count = 1;
#pragma omp parallel
{
    #pragma omp single
    {
        printf("MASTER START (%d)\n", omp_get_thread_num());
        fflush(stdout);
        for (int i = 0; i < 1000; ++i)
        {
            #pragma omp atomic
            task_count++;
            #pragma omp task
            {
                int q;
                #pragma omp atomic capture
                q = task_count--;
                printf("[Thread %d] working on task %d (%d queued)\n", omp_get_thread_num(), i, q);

如果已经有太多任务在执行中,则可以使用该信息来首先防止生成任务。现在您说,CPU任务是否完成并不重要。我对此表示怀疑,他们最终必须以某种方式完成。因此,您可能要避免产生过多的CPU任务。不幸的是,您最终会得到基于特定实现的解决方案,该解决方案基于对这种实现方式的理解。另一方面,它也可能会帮助Intel / clang使用其他主要的OpenMP库。由您自己决定,这是否比推出自己的任务队列管理更好的解决方案-是在OpenMP,pthreads,tbb还是其他之上。