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

111

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

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

#pragma omp atomic
g_qCount++;

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

#pragma omp critical
g_qCount++;

?

кодобзорансквопросы
источник

Ответы:

174

Эффект на g_qCount такой же, но сделано другое.

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

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

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

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

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

Джонатан Дурси
источник
5
«вы можете потерять портативность» - я не уверен, что это правда. В стандартной (версия 2.0) определяет , какие атомарные операции разрешены ( в основном вещи , как ++и *=) , и что , если они не поддерживаются на аппаратном уровне , они могут быть заменены на criticalсекции.
Dan R
@DanRoche: Да, вы совершенно правы. Не думаю, что это утверждение когда-либо было правильным, сейчас исправлю.
Джонатан Дурси
Несколько дней назад я следил за учебником по OpenMP, и, насколько я понял, есть разница в двух разных кодах. То есть результат может отличаться, так как критическая секция гарантирует, что инструкция выполняется потоком раз, однако возможно, что инструкция: g_qCount = g_qCount + 1; для потока 1 результат g_qCount просто сохраняется только в буфере записи, а не в памяти RAM, а когда поток 2 извлекает значение g_qCount, он просто считывает его в RAM, а не в буфере записи. Атомарная инструкция гарантирует, что инструкция сбрасывает данные в память
Giox79,
31

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

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

Майкл
источник
13
Это лучше было бы комментарием (или редактированием) предыдущего ответа.
kynan
20

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

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

  • помедленнее!

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

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

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

эфарсаракис
источник
9
Но этот ответ очень удобочитаем и будет отличным
обобщением
7

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

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

Андрий
источник
2
Я не согласен со всеми цифрами, которые вы упомянули в своем объяснении. Предполагая, что x86_64, атомарная операция будет иметь накладные расходы в несколько циклов (синхронизация строки кеша) по стоимости примерно одного цикла. Если бы в противном случае у вас была бы стоимость «истинного совместного использования», накладные расходы равны нулю. Критическая секция влечет за собой стоимость блокировки. В зависимости от того, взята ли уже блокировка или нет, накладные расходы составляют примерно 2 атомарных инструкции ИЛИ два запуска планировщика и время ожидания - обычно это значительно больше, чем в 200 раз.
Клаас ван Генд
6

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

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

xbinop =expr

x++

++x

x--

--x

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

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

Дарда
источник
1
Это еще не все, у нас есть другие расширенные атомарные директивы, такие как: #pragma omp aromic update (или чтение, обновление, запись, захват), так что это позволяет нам иметь другое полезное утверждение
бедня
1

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

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

Чтобы синхронизировать потоки в многопоточной программе, мы будем использовать 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

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

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

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

Основное различие между критическим и атомарным заключается в том, что:

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

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

гексфей
источник
-5

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

Махеш
источник
13
Это не что иное, как неудачное повторение принятого ответа без объяснения причин.
High Performance Mark
-5

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

критическая секция - это блокировка блока кода

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

Виссам Й. Халил
источник
Это неправильно. Пожалуйста, не говорите о том, чего не понимаете.
jcsahnwaldt Reinstate Monica