Можно ли заставить поток присоединиться к региону «параллельно для» после своей работы? - PullRequest
6 голосов
/ 13 июня 2019

У меня есть две задачи, которые сначала должны выполняться одновременно:

1) для цикла, который можно распараллелить

2) функция, которая может выполняться с одним потоком

Теперь позвольте мне описать, что я хочу сделать.

Если существует 8 доступных потоков,

job (1) и job (2) сначала должны одновременно запустить с 7 потоками и 1 потоком соответственно.

После завершения задания (2) поток, который использовал задание (2), должен быть назначен заданию (1), которое является параллельным циклу for.

Я использую omp_get_thread_num , чтобы подсчитать, сколько потоков активно в каждом регионе.Я ожидаю, что число потоков в job(1) увеличивается на 1, когда job(2) заканчивается.

Ниже описывается решение, которое может быть неправильным или нормальным:

  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());
        }
      }
    }
  }

Как можно сделатьработа, которую я хочу достичь, будет выполнена?

Ответы [ 3 ]

4 голосов
/ 13 июня 2019

А что-то вроде этого?

#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();
    }
}

Я не проверял это, но сингл будет выполняться только одним потоком, в то время как все остальные перейдут непосредственно в цикл for благодаря nowait.Также я думаю, что это легче читать, чем с разделами.

2 голосов
/ 13 июня 2019

Другой способ (и, возможно, лучший способ) выразить это - использовать задачи 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());
    }
}
2 голосов
/ 13 июня 2019

Проблема связана с синхронизацией.В конце 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");
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...