В чем разница между атомарным и критическим в OpenMP?
я могу это сделать
#pragma omp atomic
g_qCount++;
но это не то же самое, что
#pragma omp critical
g_qCount++;
?
источник
В чем разница между атомарным и критическим в OpenMP?
я могу это сделать
#pragma omp atomic
g_qCount++;
но это не то же самое, что
#pragma omp critical
g_qCount++;
?
Эффект на g_qCount такой же, но сделано другое.
Критический раздел OpenMP является полностью общим - он может окружать любой произвольный блок кода. Однако за эту универсальность вы платите значительными накладными расходами каждый раз, когда поток входит и выходит из критического раздела (помимо внутренних затрат на сериализацию).
(Кроме того, в OpenMP все безымянные критические секции считаются идентичными (если вы предпочитаете, есть только одна блокировка для всех безымянных критических секций), так что если один поток находится в одной [безымянной] критической секции, как указано выше, ни один поток не может войти в какую-либо [безымянный] критический раздел. Как вы могли догадаться, это можно обойти, используя именованные критические разделы).
Атомарная операция имеет гораздо меньшие накладные расходы. Там, где это возможно, он использует преимущества оборудования, обеспечивающего (скажем) операцию атомарного приращения; в этом случае нет необходимости в блокировке / разблокировке при входе / выходе из строки кода, он просто выполняет атомарное приращение, которое, по словам аппаратного обеспечения, вам нельзя мешать.
Плюсы в том, что накладные расходы намного ниже, и один поток, выполняющий атомарную операцию, не блокирует какие-либо (разные) атомные операции, которые могут произойти. Обратной стороной является ограниченный набор операций, которые поддерживает atomic.
Конечно, в любом случае вы несете расходы на сериализацию.
++
и*=
) , и что , если они не поддерживаются на аппаратном уровне , они могут быть заменены наcritical
секции.В OpenMP все безымянные критические разделы являются взаимоисключающими.
Наиболее важное различие между критическим и атомарным является то, что атомарный может защитить только одно присвоение, и вы можете использовать его с определенными операторами.
источник
Критический раздел:
Может быть расширен для сериализации групп блоков при правильном использовании тега «name».
помедленнее!
Атомарная операция:
Намного быстрее!
Обеспечивает только сериализацию определенной операции.
источник
Самый быстрый способ не является ни критическим, ни атомарным. Примерно добавление с критическим сечением в 200 раз дороже простого добавления, атомарное добавление в 25 раз дороже простого добавления.
Самый быстрый вариант (не всегда применимый) - дать каждому потоку свой собственный счетчик и выполнить операцию сокращения, когда вам нужна общая сумма.
источник
Ограничения
atomic
важны. Они должны быть подробно описаны в спецификациях OpenMP . MSDN предлагает краткую шпаргалку, так как я не удивлюсь, если это не изменится. (Visual Studio 2012 имеет реализацию OpenMP с марта 2002 г.) Процитируем MSDN:Я рекомендую использовать,
atomic
когда вы можете, и иначе называли критические разделы. Назвать их важно; таким образом вы избежите головной боли отладки.источник
Здесь уже отличные объяснения. Однако мы можем погрузиться немного глубже. Чтобы понять основную разницу между концепциями атомарного и критического разделов в OpenMP, мы должны сначала понять концепцию блокировки . Давайте рассмотрим, почему нам нужно использовать блокировки .
Чтобы синхронизировать потоки в многопоточной программе, мы будем использовать lock . Когда требуется ограничить доступ только одним потоком за раз, в игру вступают блокировки . Реализация концепции блокировки может варьироваться от процессора к процессору. Давайте выясним, как может работать простая блокировка с алгоритмической точки зрения.
Данный алгоритм может быть реализован на аппаратном языке следующим образом. Мы предположим, что это один процессор, и проанализируем поведение блокировок в нем. Для этой практики предположим, что один из следующих процессоров: MIPS , Alpha , ARM или Power .
Эта программа вроде бы в порядке, но это не так. Приведенный выше код страдает от предыдущей проблемы; синхронизация . Давайте найдем проблему. Предположим, что начальное значение блокировки равно нулю. Если два потока запускают этот код, один может достичь SW R1, lock до того, как другой прочитает переменную блокировки . Таким образом, они оба думают, что замок свободный. Чтобы решить эту проблему, есть другая инструкция, а не простые LW и SW . Это называется инструкцией чтения-изменения-записи . Это сложная инструкция (состоящая из подинструкций), которая гарантирует, что процедура получения блокировки выполняется только одним поток за раз. Отличие команд чтения-изменения-записи от простых инструкций чтения и записи состоит в том, что в нем используется другой способ загрузки и сохранения.. Он использует LL (Load Linked) для загрузки переменной блокировки и SC (Store Conditional) для записи в переменную блокировки. Дополнительный регистр связи используется для обеспечения того, чтобы процедура получения блокировки выполнялась одним потоком. Алгоритм представлен ниже.
Когда регистр ссылки сбрасывается, если другой поток предположил, что блокировка свободна, он не сможет снова записать увеличенное значение в блокировку. Таким образом достигается одновременный доступ к переменной блокировки .
Основное различие между критическим и атомарным заключается в том, что:
Использование новой переменной для блокировок приведет к критическому разделу , в то время как использование фактической переменной в качестве блокировки приведет к критической директиве, когда более сложная в вычислительном отношении область выполняется интенсивным разделом. атомарной концепции. Критический раздел полезен, когда мы выполняем много вычислений (более одной строки) над фактической переменной. Это потому, что, если результат этих вычислений не может быть записан в фактическую переменную, всю процедуру следует повторить для вычисления результатов. Это может привести к снижению производительности по сравнению с ожиданием снятия блокировки перед входом в высокопроизводительную область. Таким образом, рекомендуется использовать директиву atomic всякий раз, когда вы хотите выполнить одно вычисление (x ++, x--, ++ x, --x и т. Д.) И использовать
источник
atomic относительно эффективен, когда вам нужно включить взаимное исключение только для одной инструкции, подобное не относится к omp critical.
источник
atomic - это отдельный оператор. Критический раздел, т.е. вы блокируете выполнение одного оператора.
критическая секция - это блокировка блока кода
Хороший компилятор переведет ваш второй код так же, как и первый.
источник