Как работает директива SECTIONS в дистрибутиве OpenMP?
В OpenMP при использовании omp sections
будут ли потоки распределяться по блокам внутри разделов или каждый поток будет назначен каждому разделу?
Когда nthreads == 3
:
#pragma omp sections
{
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
}
Вывод:
id=1
id=1
Но когда я выполняю следующий код:
#pragma omp sections
{
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
}
#pragma omp sections
{
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
}
Вывод:
id=1
id=1
id=2
id=2
Из этого вывода я не могу понять, что такое понятие разделов в OpenMP.
Ответы
Ответ 1
Я очень удивлен, что ни один из вопросов здесь не ответил на OP. Поскольку это один из первых результатов в google, я думаю, что важно прояснить эту тему, особенно для новичков, которые могут приземлиться на этой странице и быть еще более запутанными. Более того, некоторые ответы говорят о предложении nowait, о котором даже не упоминается в вопросе (я думаю, это произошло после редактирования вопроса). Это делает вещи еще более запутанными.
ОП удивлен тем, что он получает одинаковый идентификатор для разных разделов. В то время как wump корректно указывает, что порядок не является детерминированным, здесь причина намного проще: код, отправленный OP, никогда не будет выполняться параллельно, потому что ключевое слово "parallel" не появляется, если оно не заключено в параллель директивы. Поэтому это не очень полезный пример. Тот факт, что OP получил идентификаторы, отличные от 0, показывает, что, вероятно, его код был встроен в параллельную директиву. Однако это не ясно из его поста и может путать начинающих.
Минимальным разумным примером является (для первого примера, отправленного OP):
#pragma omp parallel sections
{
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
#pragma omp section
{
printf ("id = %d, \n", omp_get_thread_num());
}
}
На моей машине это печатает
id = 0,
id = 1,
показывающий, что две секции выполняются разными потоками. Стоит отметить, что, однако, этот код не может извлекать больше parallelism, чем два потока: если он выполняется с большим количеством потоков, другие потоки не имеют никакой работы и будут просто сидеть без дела.
Ответ 2
Идея параллельных разделов - дать компилятору намек на то, что различные (внутренние) секции могут выполняться параллельно, например:
#pragma omp parallel sections
{
#pragma omp section
{
/* Executes in thread 1 */
}
#pragma omp section
{
/* Executes in thread 2 */
}
#pragma omp section
{
/* Executes in thread 3 */
}
/* ... */
}
Это подсказка для компилятора и не гарантируется, хотя это и должно произойти. Ваш результат - это то, что ожидается; он говорит, что в thread id 1 выполняется #sections и в потоке 2. Порядок вывода не является детерминированным, поскольку вы не знаете, какой поток будет выполняться в первую очередь.
Ответ 3
Измените первую строку из
#pragma omp разделы
в
#pragma omp параллельные секции
Директива "parallel" гарантирует, что обе секции назначены двум потокам.
Затем вы получите следующий вывод:
id = 0,
id = 1,
Ответ 4
Вам не хватает ключевого слова parallel
.
Ключевое слово parallel
запускает запуск openmp параллельно.
Ответ 5
В соответствии с OpenMP standard 3.1, раздел 2.5.2 (акцент мой):
Конструкция разделов - это нетериративная конструкция коллекционирования, которая содержит набор структурированных блоков, которые должны быть распределены между и выполняется потоками в команде. Каждый структурированный блок , выполняемый один раз одним из потоков в команде в контексте его неявная задача.
...
Каждому структурированному блоку в конструкции разделов предшествует, за исключением, возможно, первого блока, для которого директива предшествующего раздела является необязательной. Метод планирования Структурированные блоки среди потоков в команде - это реализация определяется. В конце разделов есть неявный барьер, если не указано условие nowait.
Итак, применяя эти правила к вашему делу, мы можем утверждать, что:
- различные структурированные блоки, идентифицированные в директиве
sections
, выполняются один раз одним потоком. Другими словами, у вас всегда четыре отпечатка, в зависимости от количества потоков
- блоки в первом
sections
будут выполняться (в недетерминированном порядке) до блоков во втором sections
(также выполняемом в недетерминированном порядке). Это связано с неявным барьером в конце конструкций совместного использования.
- планирование реализовано, так что вы не можете контролировать, какой поток был назначен данному разделу
Таким образом, ваш результат связан с тем, как планировщик решил назначить разные блоки для потоков в команде.
Ответ 6
Может оказаться полезным добавить дополнительную информацию в выходную строку и добавить дополнительные разделы (если у вас есть счетчик потоков)
#pragma omp parallel sections
{
#pragma omp section
{
printf ("section 1 id = %d, \n", omp_get_thread_num());
}
#pragma omp section
{
printf ("section 2 id = %d, \n", omp_get_thread_num());
}
#pragma omp section
{
printf ("section 3 id = %d, \n", omp_get_thread_num());
}
}
Затем вы можете получить более интересный вывод следующим образом:
section 1 id = 4,
section 3 id = 3,
section 2 id = 1,
который показывает, как разделы могут выполняться в любом порядке, любым доступным потоком.
Ответ 7
Обратите внимание, что 'nowait' сообщает компилятору, что потокам не нужно ждать до exit раздела. В Fortran 'nowait' идет в конце цикла или секции, что делает это более очевидным.
Ответ 8
Если вы действительно хотите начать разные потоки в разных разделах, предложение nowait
сообщает компилятору, что потокам не нужно ждать, чтобы войти в раздел.
#pragma omp parallel sections nowait
{
...
}