Яка різниця між атомною та критичною у OpenMP?


Відповіді:


173

Ефект для g_qCount однаковий, але те, що зроблено, відрізняється.

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

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

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

Недоліки полягають у тому, що накладні витрати значно нижчі, і одна нитка, яка знаходиться в атомній операції, не блокує жодних (різних) атомних операцій, які вже не відбудуться. Мінусом є обмежений набір операцій, який підтримує атом.

Звичайно, в будь-якому випадку ви несете витрати на серіалізацію.


5
"Ви могли втратити портативність" - я не впевнений, що це правда. У стандартній (версія 2.0) визначає , які атомарні операції дозволені ( в основному речі , як ++і *=) , і що , якщо вони не підтримуються на апаратному рівні , вони можуть бути замінені на criticalсекції.
Dan R

@DanRoche: Так, ти абсолютно прав. Я не думаю, що це твердження було ніколи правильним, я зараз його виправлю.
Джонатан Дурсі

Кілька днів тому я слідував підручником OpenMP, і, наскільки я зрозумів, є різниця у двох різних кодах. Тобто результат може відрізнятися, оскільки критичний розділ гарантує, що інструкція виконується потоком за часом, однак можливо, що інструкція: g_qCount = g_qCount + 1; для потоку 1 просто зберігається результат g_qCount тільки в буфері запису, а не в пам'яті оперативної пам'яті, а коли потік 2 отримує значення g_qCount, він просто зчитує значення в оперативній пам'яті, а не в буфера. Атомна інструкція запевняє, що інструкція
заповнила

31

У OpenMP всі неназвані критичні розділи взаємовиключні.

Найважливіша відмінність між критичною та атомною полягає в тому, що атомний може захищати лише одне призначення, і ви можете використовувати його з конкретними операторами.


13
Краще це буде коментар (або редакція) попередньої відповіді.
кинан

20

Критичний розділ:

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

  • Повільніше!

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

  • Значно швидше!

  • Тільки забезпечує серіалізацію певної операції.


9
Але ця відповідь дуже читабельна і була б чудовою підсумком першої відповіді
Michał Miszczyszyn

7

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

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


2
Я не згоден з усіма цифрами, які ви згадуєте у своєму поясненні. Якщо припустити x86_64, атомна операція матиме декілька накладних витрат (синхронізація лінії кеша) на вартість приблизно циклу. Якщо у вас виникне вартість "справжнього спільного використання" в іншому випадку, накладні витрати - нігіл. Критичний розділ спричиняє вартість блокування. Залежно від того, заблоковано вже зроблено чи ні, накладні витрати становлять приблизно 2 атомні інструкції АБО два запуски планувальника та час сну - зазвичай це буде значно більше 200x.
Клаас ван Генд

6

atomicВажливі обмеження . Вони повинні бути детально описані у специфікаціях OpenMP . MSDN пропонує швидкий шпаргалка, тому що я не здивуюсь, якщо це не зміниться. (Visual Studio 2012 має реалізацію OpenMP з березня 2002 року.) Для цитування MSDN:

Вираз виразу повинен мати одну з таких форм:

xбінок =expr

x++

++x

x--

--x

У попередніх виразах: xце lvalueвираз зі скалярним типом. expr- це вираз зі скалярним типом, і він не посилається на об'єкт, позначений x. бінарний оператор не є перевантаженим оператор і є одним з +, *, -, /, &, ^, |, <<, або >>.

Я рекомендую використовувати, atomicколи ви можете і інакше назвати критичні розділи. Назвати їх важливо; ви уникнете налагодження головного болю таким чином.


1
Це ще не все, у нас є інші вдосконалені атомні директиви, такі як: #pragma omp aromic update (або читати, використовувати, записувати, фіксувати), тому це дозволяє нам мати ще одне корисне твердження
badia

1

Тут уже чудові пояснення. Однак ми можемо зануритися трохи глибше. Щоб зрозуміти основну різницю між атомними та критичними розділами в OpenMP, ми повинні спершу зрозуміти концепцію блокування . Давайте розглянемо, чому нам потрібно використовувати замки .

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

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

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, заблокувати, перш ніж інший зчитує змінну блокування . Таким чином, вони обидва думають, що замок вільний. Для вирішення цього питання існує ще одна інструкція, а не проста LW та SW . Вона називається інструкцією Read-Modify-Write . Це складна інструкція (що складається з підінструкцій), яка запевняє, що процедуру придбання блокування виконує лише однанитка за раз. Відмінність Read-Modify-Writeпорівняно з простими вказівками для читання та запису є те, що він використовує інший спосіб завантаження та зберігання . Він використовує 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

Коли реєстр посилань буде скинутий, якщо інший потік вважав, що замок вільний, він не зможе знову записати збільшене значення до блокування. Таким чином, паралельність доступу до замка набувається змінної .

Основна різниця між критичною та атомною полягає в тому, що:

Навіщо використовувати locks (нову змінну), тоді як ми можемо використовувати фактичну змінну (яку ми виконуємо над нею), як змінну блокування?

Використання нової змінної для блокування призведе до критичного розділу , тоді як використання фактичної змінної як блокування призведе до атомної концепції. Критичний розділ корисний, коли ми виконуємо велику кількість обчислень (більше одного рядка) на фактичній змінній. Це тому, що якщо результат цих обчислень не вдасться записати на фактичну змінну, для обчислення результатів слід повторити всю процедуру. Це може призвести до низької продуктивності порівняно з очікуванням звільнення блокування перед входом у високо обчислювальний регіон. Таким чином, рекомендується використовувати критичну директиву, коли інтенсивний розділ робиться більш складно обчислювальною областю. атомну директиву всякий раз, коли потрібно виконати єдиний обчислення (x ++, x--, ++ x, --x тощо) та використовувати


-5

atomic - це відносно ефективність роботи, коли вам потрібно включити взаємне виключення лише для однієї інструкції, аналогічна не стосується критичного omp.


13
Це не що інше, як неправдиве перерахування прийнятої відповіді без пояснень.
Марка високої продуктивності

-5

atomic - це один критичний розділ твердження, тобто ви блокуєте для виконання одного оператора

критичний розділ - це блокування блоку коду

Хороший компілятор переведе ваш другий код так само, як і перший


Це просто неправильно. Не кажіть про речі, які ви не розумієте.
jcsahnwaldt Відновити Моніку
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.