OpenMP - запускать один регион с nowait и после объединения других потоков в цикл for - PullRequest
0 голосов
/ 17 октября 2019

Что я пытаюсь сделать, так это то, что первая часть функции выполняется в одном потоке, а другой поток запускает вторую функцию (эта функция имеет три цикла), а после завершения одного потока первая функция присоединяется к другим. чтобы помочь с петлями.

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

void main(){


/* code */


#pragma omp parallel
{
     #pragma omp single nowait
     {
      std::cout << "Running func 1:" << omp_get_thread_num() << std::endl;
      func1();
      std::cout << "Finish func 1" <<std::endl;
     }

#pragma omp nowait

std::cout << "Running func 2:" << omp_get_thread_num() << std::endl;
func2();

#pragma omp barrier
}//close parallel


/* more code */

}//close main



void func2(void){

        /* code to read file */ 



#pragma omp parallel
{


     for (int i=40;i<60;i++){
          #pragma omp for nowait
          for (int j=0;j<100;j++){
           /* code  */
          }
      }

#pragma omp for schedule(dynamic,1) nowait
  for (int i=0;i<40;i++){
        for (int j=0;j<100;j++){
         /* code  */
       }
  }

#pragma omp for schedule(dynamic)
  for (int i=60;i<100;i++){
        for (int j=0;j<100;j++){
         /* code  */
        }
  }

         /* code to write file */ 

}//close parallel

#pragma omp barrier
} //close func2

Мой терминал показывает:

Running func 1: 0
Running func 2: 1
Running func 2: 2
Running func 2: 3 
Finish func 1
Running func 2: 0 

Edit

Obs .: Func1 должен выполняться только в одном потоке

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

Используя код, предложенный Джимом Коуни, func1 и func2 выполняются параллельно, однако либо потоки запускают func2 дважды, либо только один поток запускается самостоятельно без помощи других. .

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

1 Ответ

0 голосов
/ 18 октября 2019

Ваш код имеет ряд проблем

  1. Нет такой директивы openMP, как #pragma omp nowait, так что вы можете даже не компилировать с включенным OpenMP (так как, когда это так, вы должны получитьсообщение об ошибке, например, см. https://godbolt.org/z/EbYV6h)
  2. Никогда не требуется #pragma omp barrier непосредственно перед концом параллельной области (так как главный поток, который будет выполнять следующую последовательную областьне может выйти, пока все потоки также не закончат выполнение в параллельной области.)

Я не понимаю, почему вы хотите использовать вложенный параллелизм. Вы уже выполняете func2 () параллельно, поэтому любое вложение здесь приведет к избыточной подписке.

Вы можете достичь того, что хотите, либо так:

#pragma omp parallel
{
#pragma omp single nowait
    func1()
  func2();
}

void func2()
{
#pragma omp for schedule(dynamic), nowait
    for (...)
        ... etc ...
}

Или, используя задачии Taskloops, который является потенциально более чистым способом выразить это.

Использование задач, (и, после вашего разъяснения, вы хотите, чтобы function2 выполнялся только один раз (я читал то, что сказал код, поскольку это проще, чемчтение мыслей!)), что-то вроде этого работает

#include <unistd.h>
#include <stdio.h>
#include <omp.h>

void function1()
{
  fprintf(stderr,"%d: entering function1\n", omp_get_thread_num());
  sleep(1);
  fprintf(stderr,"%d: leaving function1\n", omp_get_thread_num());
}

void function2()
{
  fprintf(stderr,"%d: entering function2\n", omp_get_thread_num());
#pragma omp taskloop grainsize(1)                                                                                   
  for (int i=0; i<10; i++)
    {
      fprintf(stderr,"%d: starting iteration %d\n",
                     omp_get_thread_num(),i);
      sleep(1);
      fprintf(stderr,"%d: finishing iteration %d\n",
                     omp_get_thread_num(),i);
    }
  fprintf(stderr,"%d: leaving function2\n", omp_get_thread_num());
}

int main()
{
#pragma omp parallel
  {
#pragma omp single
    {
      fprintf(stderr,"Executing with %d threads\n",
                      omp_get_num_threads());
#pragma omp task
      {
        function1();
      }
#pragma omp task
      {
        function2();
      }
    }
  }
}

Вот выполнение на четырех потоках, конечно, возможны другие чередования.

OMP_NUM_THREADS=4 ./a.out
Executing with 4 threads
3: entering function2
2: entering function1
0: starting iteration 0
1: starting iteration 1
3: starting iteration 9
1: finishing iteration 1
3: finishing iteration 9
0: finishing iteration 0
3: starting iteration 8
1: starting iteration 2
2: leaving function1
0: starting iteration 3
2: starting iteration 4
3: finishing iteration 8
1: finishing iteration 2
3: starting iteration 7
0: finishing iteration 3
0: starting iteration 6
2: finishing iteration 4
1: starting iteration 5
0: finishing iteration 6
3: finishing iteration 7
1: finishing iteration 5
3: leaving function2

Вы можете видеть, что только один потоквыполняет каждую из функций [12], и итерации цикла являются общими для всех потоков.

...