Вопрос о разделах OpenMP и критических - PullRequest
0 голосов
/ 28 ноября 2018

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

Вот что я сейчас делаю:

#pragma omp parallel for
for (i = 0; i < n; ++i)
{
  ... build array bi ...
  #pragma omp critical
  {
    update_matrix(A, bi)
  }
}

...

subroutine update_matrix(A, b)
{
  printf("id0 = %d\n", omp_get_thread_num());
  #pragma omp parallel sections
  {
    #pragma omp section
    {
      printf("id1 = %d\n", omp_get_thread_num());
      modify columns 1 to j of A using b
    }

    #pragma omp section
    {
      printf("id2 = %d\n", omp_get_thread_num());
      modify columns j+1 to k of A using b
    }
  }
}

Проблема в том, что два разных раздела процедуры update_matrix () не распараллеливаются.Вывод, который я получаю, выглядит следующим образом:

id0 = 19
id1 = 0
id2 = 0
id0 = 5
id1 = 0
id2 = 0
...

Таким образом, два раздела выполняются одним потоком (0).Я попытался удалить критический элемент #pragma в основном цикле, но он дает тот же результат.Кто-нибудь знает, что я делаю не так?

1 Ответ

0 голосов
/ 29 ноября 2018

#pragma omp parallel sections не должен работать там, потому что вы уже находитесь в параллельной части кода, распространяемой предложением #pragma omp prallel for.Если вы не включили вложенное распараллеливание с помощью omp_set_nested(1);, предложение parallel sections будет игнорироваться.

Обратите внимание, что оно не обязательно эффективно, так как порождение новых потоков имеет накладные расходы, которые могут не стоить, если update_matrix деталь не слишком загружает процессор.

У вас есть несколько вариантов:

  • Забудьте об этом.Если некритическая часть цикла действительно является той, которая занимает большинство вычислений, и у вас уже есть столько потоков, сколько процессоров, то добавление дополнительных потоков для простых операций не принесет пользы.Просто удалите предложение parallel sections в подпрограмме.

  • Попробуйте включить вложение с помощью omp_set_nested(1);

  • Другой вариант, который предоставляется за платудвойной накладные расходы синхронизации и будет использовать именованные критические секции.В секции critical ONE_TO_J может быть только один поток, а в секции critical J_TO_K - один, поэтому в основном до двух потоков могут обновлять матрицу параллельно.Это дорого с точки зрения затрат на синхронизацию.

    #pragma omp parallel for
    for (i = 0; i < n; ++i)
    {
      ... build array bi ...
      update_matrix(A, bi); // not critical
    }
    
    ...
    
    subroutine update_matrix(A, b)
    {
      printf("id0 = %d\n", omp_get_thread_num());
        #pragma omp critical(ONE_TO_J)
        {
          printf("id1 = %d\n", omp_get_thread_num());
          modify columns 1 to j of A using b
        }
    
        #pragma omp critical(J_TO_K)
        {
          printf("id2 = %d\n", omp_get_thread_num());
          modify columns j+1 to k of A using b
        }
    }
    
  • Или используйте атомарные операции для редактирования матрицы, если это подходит.

    #pragma omp parallel for
    for (i = 0; i < n; ++i)
    {
      ... build array bi ...
      update_matrix(A, bi); // not critical
    }
    
    ...
    
    subroutine update_matrix(A, b)
    {
        float tmp;
        printf("id0 = %d\n", omp_get_thread_num());
        for (int row=0; row<max_row;row++)
            for (int column=0;column<k;column++){
                float(tmp)=some_function(b,row,column);
                #pragma omp atomic
                A[column][row]+=tmp;
                }
    
    }
    

    Кстатиданные хранятся в мажорном порядке в C, поэтому вы должны обновлять матрицу строка за строкой, а не столбец за столбцом.Это предотвратит ложное совместное использование и улучшит производительность доступа к памяти алгоритма.

...