2015-10-30 3 views
13

Я пытаюсь понять точное различие между #pragma omp critical и #pragma omp single в OpenMP:разница между OMP критическим и OMP одного

определения Microsoft для них являются:

  • Single: Позволяет указать, что в разделе код должен быть выполнен на одной нитью, не обязательно главной нитью.
  • Критический: указывает, что код выполняется только в одном потоке с .

Таким образом, это означает, что в обоих случаях точный раздел кода будет выполняться только одним потоком, а другие потоки не войдут в этот раздел, например. если мы что-то напечатаем, мы увидим результат на экране один раз, правильно?

Как насчет разницы? Похоже, что критические заботятся о времени исполнения, но не одиноки! Но я не вижу никакой разницы на практике! Означает ли это, что вид ожидания или синхронизации для других потоков (которые не входят в этот раздел) рассматривается в критическом состоянии, но нет ничего, что удерживало бы другие потоки в одном? Как это может изменить результат на практике?

Я ценю, если кто-нибудь может прояснить это мне, особенно на примере. Благодаря!

ответ

28

single и critical это два очень разные вещи. Как вы упомянули:

  • single указывает, что часть кода должна быть выполнена по одной нити (не обязательно мастер резьбы)
  • critical указывает, что код выполняется одним потоком одновременно

так бывший будет выполняться только один раз , а позже будет выполняться столько раз, сколько есть в Тре объявления.

Например, следующий код

int a=0, b=0; 
#pragma omp parallel num_threads(4) 
{ 
    #pragma omp single 
    a++; 
    #pragma omp critical 
    b++; 
} 
printf("single: %d -- critical: %d\n", a, b); 

напечатает

single: 1 -- critical: 4 

Я надеюсь, что вы видите разницу лучше.

Ради полноты картины, я могу добавить, что:

  • master очень похож на single с двумя отличиями:
    1. master будут выполняться мастером только тогда, когда single может быть выполнена какая нить достигает области сначала; и
    2. single имеет неявный барьер после завершения области, где все потоки ожидают синхронизации, а master - нет.
  • atomic очень похож на critical, но ограничен для выбора простых операций.

Я добавил эти Precisions, так как эти две пары команд часто те люди, как правило, неразбериха ...

+0

Большое спасибо. Я понял! Итак, можем ли мы сказать: критический - это своего рода щит, чтобы избежать состояния гонки, но единый для легких работ, которые нужно делать один раз? Если мы ставим критический параметр в if-statement, который позволяет просто вставить один конкретный поток, то он может работать аналогично одиночному? – Amir

+0

Да, критически важно избегать условий гонки. Его типичное использование, если для замены предложения 'reduce()', когда переменная для уменьшения является массивом, как в C и C++, предложения 'reduce' применимы только к скалярным переменным. Что касается размещения критического значения внутри оператора if, как вы описываете, я действительно не вижу смысла ... – Gilles

+0

Я думаю, что OP просто хотел узнать, эквивалентен ли он (если для одного потока с критическим и единственным) , Многие из предложений OpenMP можно эмулировать с помощью других предложений. Я думаю, было бы интересно увидеть минимальный набор предложений для подражания всем или большинству предложений. Иногда бывает полезно знать такие вещи. Бывают случаи, когда предложения OpenMP являются слишком ограничительными, и полезно знать, как это сделать (без использования другого интерфейса потоков, такого как pthreads). Самый распространенный пример, который я могу придумать, - это сделать индивидуальное сокращение. –

22

single и critical принадлежат к двум совершенно разным классам OpenMP конструкций. single представляет собой конструкцию для обрезки, наряду с for и sections. Конструкции Worksharing используются для распределения определенной работы между потоками. Такие конструкции являются «коллективными» в том смысле, что в правильных программах OpenMP все потоки должны сталкиваться с ними при выполнении и, кроме того, в том же порядке, включая также конструкции barrier. Три распараллеливание конструкции охватывают три общих случая:

  • for (а.к.а. конструкция цикла) распределяет автоматически итерации цикла между нитями - в большинстве случаев все нити получают работу, чтобы сделать;
  • sections распределяет последовательность независимых блоков кода среди потоков - некоторые потоки выполняют работу. Это обобщение конструкции for, поскольку цикл с 100 итерациями может быть выражен, например, 10 секций петель с 10 итерациями каждый.
  • single выделяет блок кода для выполнения только одним потоком, часто первым, с которым он сталкивается (деталь реализации) - только один поток получает работу. single в значительной степени эквивалентен sections только с одной секцией.

Общей чертой всех распараллеливание конструкций является наличие неявного барьера на их конце, который барьер может быть выключен путем добавления оговорки к соответствующему OpenMP конструкции nowait, но стандарт не требует таких поведение и с некоторыми временами работы OpenMP барьер может оставаться там, несмотря на наличие nowait. Неправильно упорядоченные (т. Е. Вне последовательности в некоторых потоках) конструкторы совместной работы могут привести к взаимоблокировкам. Правильная программа OpenMP никогда не зациклится, когда будут присутствовать барьеры.

critical - это схема синхронизации, рядом с master, atomic и другие. Конструкции синхронизации используются для предотвращения условий гонки и для приведения порядка в исполнение вещей.

  • critical предотвращает условия гонки, предотвращая одновременное выполнение кода между потоками в так называемой конкурирующей группы. Это означает все темы от все параллельные области, сталкивающиеся с аналогичными именованными критическими конструктами, сериализуются;
  • atomic превращает некоторые простые операции памяти в атомные, обычно используя специальные инструкции по сборке. Atomics завершается сразу как единый нерушимый блок. Например, атом, считываемый из некоторого местоположения одним потоком, который одновременно происходит с атомарной записью в одно и то же место другим потоком, либо вернет старое значение, либо обновленное значение, но никогда не будет промежуточным месивом бит из старого и нового значений;
  • master выделяет блок кода для выполнения главным потоком (поток с идентификатором 0). В отличие от single, в конце конструкции нет неявного барьера, и также нет требования, чтобы все потоки столкнулись с конструкцией master. Кроме того, отсутствие скрытого барьера означает, что master не очищает вид разделяемой памяти потоков (это важная, но очень слабо понятная часть OpenMP). master - это в основном сокращенная версия для if (omp_get_thread_num() == 0) { ... }.

critical очень многогранная конструкция, как она способна сериализация различных частей коды в самом разных частях программного кода, даже в разных параллельных регионах (значащих в случае только вложенного параллелизм). Каждая конструкция critical имеет необязательное имя, указанное в скобках сразу после. Анонимные критические конструкции имеют одно и то же имя для конкретной реализации. Когда поток входит в такую ​​конструкцию, любой другой поток, сталкиваясь с другой конструкцией с тем же именем, приостанавливается до тех пор, пока исходный поток не выйдет из его конструкции. Затем процесс сериализации продолжается с остальными потоками.

Ниже приведено описание вышеизложенных концепций. Следующий код:

#pragma omp parallel num_threads(3) 
{ 
    foo(); 
    bar(); 
    ... 
} 

приводит что-то вроде:

thread 0: -----< foo() >< bar() >--------------> 
thread 1: ---< foo() >< bar() >----------------> 
thread 2: -------------< foo() >< bar() >------> 

(резьба 2 намеренно опоздавший)

Имея foo(); вызов в течение single конструкции:

#pragma omp parallel num_threads(3) 
{ 
    #pragma omp single 
    foo(); 
    bar(); 
    ... 
} 

приводит к чему-то вроде:

thread 0: ------[-------|]< bar() >-----> 
thread 1: ---[< foo() >-|]< bar() >-----> 
thread 2: -------------[|]< bar() >-----> 

Здесь [ ... ] обозначает область применения single конструкции и | является неявным барьером на его конце. Обратите внимание, как поток latecomer 2 заставляет все остальные потоки ждать. В потоке 1 выполняется вызов foo() в качестве примера. Рабочая среда OpenMP решает назначить задание первому потоку, чтобы встретить конструкцию.

Добавление пункта nowait может удалить неявный барьер, в результате чего-то вроде:

thread 0: ------[]< bar() >-----------> 
thread 1: ---[< foo() >]< bar() >-----> 
thread 2: -------------[]< bar() >----> 

Имея foo(); вызов в течение анонимного critical конструкции:

#pragma omp parallel num_threads(3) 
{ 
    #pragma omp critical 
    foo(); 
    bar(); 
    ... 
} 

результатов в чем-то вроде:

thread 0: ------xxxxxxxx[< foo() >]< bar() >--------------> 
thread 1: ---[< foo() >]< bar() >-------------------------> 
thread 2: -------------xxxxxxxxxxxx[< foo() >]< bar() >---> 

С xxxxx... показано время, в течение которого поток ожидает, пока другие потоки выполнят критический конструкт с тем же именем, прежде чем он сможет ввести свою собственную конструкцию.

Критические конструкции разных имен не синхронизируются друг с другом. Например .:

#pragma omp parallel num_threads(3) 
{ 
    if (omp_get_thread_num() > 1) { 
    #pragma omp critical(foo2) 
    foo(); 
    } 
    else { 
    #pragma omp critical(foo01) 
    foo(); 
    } 
    bar(); 
    ... 
} 

приводит что-то вроде:

thread 0: ------xxxxxxxx[< foo() >]< bar() >----> 
thread 1: ---[< foo() >]< bar() >---------------> 
thread 2: -------------[< foo() >]< bar() >-----> 

Теперь поток 2 не синхронизируется с другими потоками, поскольку его критическая конструкция называется по-разному, и, следовательно, делает потенциально опасный одновременный вызов в foo().

С другой стороны, анонимные критические конструкции (и в общих конструкциях с тем же именем) синхронизировать друг с другом независимо от того, где в коде они:

#pragma omp parallel num_threads(3) 
{ 
    #pragma omp critical 
    foo(); 
    ... 
    #pragma omp critical 
    bar(); 
    ... 
} 

и в результате выполнения хронологию:

thread 0: ------xxxxxxxx[< foo() >]<...>xxxxxxxxxxxxxxx[< bar() >]------------> 
thread 1: ---[< foo() >]<...>xxxxxxxxxxxxxxx[< bar() >]-----------------------> 
thread 2: -------------xxxxxxxxxxxx[< foo() >]<...>xxxxxxxxxxxxxxx[< bar() >]-> 
Смежные вопросы