В чем разница между атомным и критическим в OpenMP? - PullRequest
98 голосов
/ 17 октября 2011

В чем разница между атомным и критическим в OpenMP?

Я могу сделать это

#pragma omp atomic
g_qCount++;

но это не то же самое, что

#pragma omp critical
g_qCount++;

Ответы [ 8 ]

157 голосов
/ 18 октября 2011

Эффект на g_qCount тот же, но то, что сделано, отличается.

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

(Кроме того, в OpenMP все неназванные критические секции считаются идентичными (если вы предпочитаете, есть только одна блокировка для всех неназванных критических секций), так что если один поток находится в одной [неназванной] критической секции, как указано вышени один поток не может войти в любую [неназванную] критическую секцию. Как вы можете догадаться, вы можете обойти это, используя именованные критические секции).

У атомарной операции намного меньше накладных расходов.Там, где это возможно, он использует преимущества аппаратного обеспечения, скажем, операции атомарного приращения;в этом случае нет необходимости в блокировке / разблокировке при входе / выходе из строки кода, он просто делает атомарный инкремент, которому аппаратные средства говорят, что вам нельзя вмешиваться.

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

Конечно, в любом случае вы платите за сериализацию.

27 голосов
/ 27 февраля 2012

В OpenMP все безымянные критические разделы являются взаимоисключающими.

Самое важное различие между критическим и атомарным состоит в том, что атомарный может защитить только одно присвоение, и вы можете использовать его с конкретными операторами.

15 голосов
/ 23 декабря 2013

Критический раздел:

  • Обеспечивает сериализацию блоков кода.
  • Может быть расширен для сериализации групп блоков с правильным использованием тега "name".

  • Медленнее!

Атомная операция:

  • намного быстрее!

  • Обеспечивает только сериализацию определенной операции.

6 голосов
/ 26 января 2014

Самый быстрый способ не является ни критическим, ни атомарным. Примерно, сложение с критическим сечением в 200 раз дороже, чем простое, атомарное добавление в 25 раз дороже простого сложения.

Самый быстрый вариант (не всегда применимый) - дать каждому потоку свой счетчик и выполнить операцию сокращения, когда вам нужна общая сумма.

5 голосов
/ 01 ноября 2013

Ограничения atomic важны.Они должны быть подробно описаны в спецификации OpenMP . MSDN предлагает быстрый шпаргалку, так как я не удивлюсь, если это не изменится.(Visual Studio 2012 имеет реализацию OpenMP с марта 2002 года.) Цитируя MSDN:

Оператор выражения должен иметь одну из следующих форм:

x binop = expr

x++

++x

x--

--x

Inпредыдущие выражения: x - это выражение lvalue со скалярным типом.expr - это выражение со скалярным типом, и оно не ссылается на объект, обозначенный x. binop не является перегруженным оператором и является одним из +, *, -, /, &, ^, |, << или >>.

Я рекомендую использовать atomic, когда вы можете, и , названный критическими разделами, в противном случае.Называть их важно;таким образом вы избежите головной боли отладки.

1 голос
/ 02 мая 2018

Уже отличные объяснения здесь. Тем не менее, мы можем погрузиться немного глубже. Чтобы понять разницу между основными понятиями atomic и критического сечения в OpenMP, нам нужно сначала понять концепцию lock . Давайте рассмотрим, почему нам нужно использовать замки .

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

Чтобы синхронизировать потоки в многопоточной программе, мы будем использовать lock . Когда доступ должен быть ограничен только одним потоком за раз, в игру вступают lock s. Реализация концепции lock может варьироваться от процессора к процессору. Давайте выясним, как простая блокировка может работать с алгоритмической точки зрения.

1. Define a variable called lock.
2. For each thread:
   2.1. Read the lock.
   2.2. If lock == 0, lock = 1 and goto 3    // Try to grab the lock
       Else goto 2.1    // Wait until the lock is released
3. Do something...
4. lock = 0    // Release the lock

Данный алгоритм может быть реализован на аппаратном языке следующим образом. Мы будем использовать один процессор и проанализируем поведение блокировок в этом. Для этой практики давайте предположим один из следующих процессоров: MIPS , Alpha , ARM или Power .

try:    LW R1, lock
        BNEZ R1, try
        ADDI R1, R1, #1
        SW R1, lock

Эта программа вроде бы в порядке, но это не так. Приведенный выше код страдает от предыдущей проблемы; синхронизация * * +1044. Давайте найдем проблему. Предположим, что начальное значение блокировки равно нулю. Если два потока запускают этот код, один может достигнуть SW R1, заблокировать до того, как другой прочитает переменную lock . Таким образом, они оба думают, что блокировка 1050 * свободна. Чтобы решить эту проблему, есть другая инструкция, а не простая LW и SW . Это называется инструкция чтения-изменения-записи . Это сложная инструкция (состоящая из подинструкций), которая гарантирует, что процедура получения блокировки выполняется только одиночным потоком одновременно. Разница Read-Modify-Write по сравнению с простыми Read и Write в том, что он использует другой способ Загрузка и Сохранение . Он использует LL (Load Linked) для загрузки переменной блокировки и SC (Store Conditional) для записи в переменную блокировки. Дополнительный Link Link используется для обеспечения выполнения процедуры захвата блокировки одним потоком. Алгоритм приведен ниже.

1. Define a variable called lock.
2. For each thread:
   2.1. Read the lock and put the address of lock variable inside the Link Register.
   2.2. If (lock == 0) and (&lock == Link Register), lock = 1 and reset the Link Register then goto 3    // Try to grab the lock
       Else goto 2.1    // Wait until the lock is released
3. Do something...
4. lock = 0    // Release the lock

Когда регистр ссылки сбрасывается, если другой поток предположил, что блокировка свободна, он не сможет снова записать увеличенное значение в блокировку. Таким образом, достигается одновременный доступ к переменной lock .

Разница в ядре между критическими и атомными исходит из того, что:

Зачем использовать блокировки (новую переменную), в то время как мы можем использовать фактическую переменную (над которой мы выполняем операцию), в качестве переменной блокировки?

Использование новой переменной для блокировок приведет к критической секции , в то время как использование переменной actual в качестве блокировки приведет к атомная концепция. Критическая секция полезна, когда мы выполняем много вычислений (более одной строки) для фактической переменной. Это потому, что, если результат этих вычислений не может быть записан на фактическую переменную, вся процедура должна быть повторена для вычисления результатов. Это может привести к низкой производительности по сравнению с ожиданием снятия блокировки перед входом в область с высокой вычислительной мощностью. Таким образом, рекомендуется использовать директиву atomic всякий раз, когда вы хотите выполнить одно вычисление (x ++, x--, ++ x, --x и т. Д.) И использовать критический директива, когда интенсивный раздел выполняет более сложную вычислительную область.

0 голосов
/ 18 декабря 2013

атомарный - это критический раздел с одним оператором, т. Е. Вы блокируете выполнение одного оператора.

критический раздел - это блокировка с кодом.

Хороший компилятор переведет ваш второй код так жекак это делает первый

0 голосов
/ 26 июля 2013

atomic является относительно эффективным с точки зрения производительности, когда вам нужно включить взаимное исключение только для одной команды, аналогичной неверной в отношении критических значений omp.

...