Apa perbedaan antara Atomic dan Critical di OpenMP?

111

Apa perbedaan antara Atomic dan Critical di OpenMP?

aku bisa melakukan ini

#pragma omp atomic
g_qCount++;

tapi bukankah ini sama dengan

#pragma omp critical
g_qCount++;

?

codereviewanskquestions
sumber

Jawaban:

173

Efeknya pada g_qCount sama, tetapi apa yang dilakukan berbeda.

Bagian kritis OpenMP benar-benar umum - ia dapat mengelilingi sembarang blok kode. Namun, Anda membayar untuk keumuman itu, dengan menimbulkan overhead yang signifikan setiap kali utas masuk dan keluar dari bagian kritis (di atas biaya serialisasi yang melekat).

(Selain itu, di OpenMP semua bagian kritis yang tidak disebutkan namanya dianggap identik (jika Anda mau, hanya ada satu kunci untuk semua bagian kritis yang tidak disebutkan namanya), sehingga jika satu utas berada dalam satu bagian kritis [tanpa nama] seperti di atas, tidak ada utas yang dapat memasukkan Bagian kritis [tanpa nama]. Seperti yang Anda duga, Anda bisa menyiasatinya dengan menggunakan bagian kritis bernama).

Operasi atom memiliki overhead yang jauh lebih rendah. Jika tersedia, ia memanfaatkan perangkat keras yang menyediakan (katakanlah) operasi kenaikan atom; dalam hal ini tidak ada kunci / buka kunci yang diperlukan untuk masuk / keluar dari baris kode, itu hanya melakukan penambahan atom yang menurut perangkat keras tidak dapat Anda ganggu.

Kelebihannya adalah bahwa overhead jauh lebih rendah, dan satu utas yang berada dalam operasi atom tidak memblokir operasi atom (berbeda) apa pun yang akan terjadi. Sisi negatifnya adalah rangkaian operasi terbatas yang didukung atom.

Tentu saja, dalam kedua kasus tersebut, Anda dikenai biaya serialisasi.

Jonathan Dursi
sumber
5
"Anda bisa kehilangan portabilitas" - Saya tidak yakin ini benar. The standar (versi 2.0) menentukan yang operasi atom diperbolehkan (pada dasarnya hal-hal seperti ++dan *=) dan bahwa jika mereka tidak didukung dalam perangkat keras, mereka mungkin akan digantikan oleh criticalbagian.
Dan R
@DanRoche: Ya, Anda benar. Saya pikir pernyataan itu tidak pernah benar, saya akan memperbaikinya sekarang.
Jonathan Dursi
Beberapa hari yang lalu saya mengikuti tutorial OpenMP, dan sejauh yang saya mengerti, ada perbedaan pada dua kode yang berbeda. Hasilnya bisa berbeda karena bagian kritis memastikan bahwa instruksi dieksekusi oleh thread pada suatu waktu, namun ada kemungkinan bahwa instruksi: g_qCount = g_qCount + 1; untuk utas 1 cukup menyimpan hasil g_qCount hanya di writebuffer bukan di memori RAM, dan ketika utas 2 mengambil nilai g_qCount, ia cukup membaca satu di RAM, bukan di writebuffer. Instruksi atom memastikan instruksi membuang data ke memori
Giox79
31

Di OpenMP, semua bagian penting yang tidak disebutkan namanya saling eksklusif.

Perbedaan terpenting antara critical dan atomic adalah atomic hanya dapat melindungi satu tugas dan Anda dapat menggunakannya dengan operator tertentu.

Michael
sumber
13
Ini lebih baik menjadi komentar (atau edit) dari jawaban sebelumnya.
kynan
20

Bagian penting:

  • Memastikan serialisasi blok kode.
  • Dapat diperluas untuk membuat serial blok dengan penggunaan tag "nama" yang tepat.

  • Lebih lambat!

Operasi atom:

  • Jauh lebih cepat!

  • Hanya memastikan serialisasi operasi tertentu.

efarsarakis
sumber
9
Tetapi jawaban ini sangat mudah dibaca dan akan menjadi ringkasan yang bagus dari jawaban pertama
Michał Miszczyszyn
7

Cara tercepat bukanlah cara kritis maupun atom. Kira-kira, penjumlahan dengan penampang kritis 200 kali lebih mahal daripada penjumlahan sederhana, penjumlahan atom 25 kali lebih mahal daripada penjumlahan sederhana.

Opsi tercepat (tidak selalu berlaku) adalah memberi setiap utas penghitungnya sendiri dan membuat operasi pengurangan ketika Anda membutuhkan jumlah total.

Andrii
sumber
2
Saya tidak setuju dengan semua angka yang Anda sebutkan dalam penjelasan Anda. Dengan asumsi x86_64, operasi atom akan memiliki beberapa overhead siklus (menyinkronkan baris cache) dengan biaya kira-kira satu siklus. Jika Anda memiliki biaya '' pembagian yang sebenarnya '' sebaliknya, biaya overhead nihil. Bagian kritis menimbulkan biaya kunci. Tergantung pada apakah kunci sudah diambil atau belum, overhead kira-kira 2 instruksi atom ATAU dua penjadwalan dan waktu tidur - yang biasanya akan jauh lebih dari 200x.
Klaas van Gend
6

Keterbatasan atomicitu penting. Mereka harus dirincikan tentang spesifikasi OpenMP . MSDN menawarkan lembar contekan cepat karena saya tidak akan terkejut jika ini tidak berubah. (Visual Studio 2012 memiliki implementasi OpenMP dari Maret 2002.) Mengutip MSDN:

Pernyataan ekspresi harus memiliki salah satu bentuk berikut:

xbinop =expr

x++

++x

x--

--x

Dalam ekspresi sebelumnya: xadalah lvalueekspresi dengan tipe skalar. expradalah ekspresi dengan tipe skalar, dan tidak mereferensikan objek yang ditunjuk x. binop tidak operator overload dan merupakan salah satu dari +, *, -, /, &, ^, |, <<, atau >>.

Saya merekomendasikan untuk menggunakan atomicbila Anda bisa dan menamai bagian kritis sebaliknya. Memberi nama mereka penting; Anda akan menghindari sakit kepala debugging dengan cara ini.

darda
sumber
1
Ini belum semuanya, kami memiliki arahan atomik lanjutan lainnya seperti: #pragma omp pembaruan aromik (atau baca, perbarui, tulis, tangkap) sehingga memungkinkan kami memiliki beberapa pernyataan bermanfaat lainnya
pooria
1

Penjelasannya sudah bagus disini. Namun, kita bisa menyelam lebih dalam. Untuk memahami perbedaan inti antara konsep bagian atom dan kritis di OpenMP, kita harus memahami konsep kunci terlebih dahulu. Mari kita tinjau mengapa kita perlu menggunakan kunci .

Program paralel dijalankan oleh banyak utas. Hasil deterministik akan terjadi jika dan hanya jika kita melakukan sinkronisasi antara utas ini. Tentu saja, sinkronisasi antar utas tidak selalu diperlukan. Kami mengacu pada kasus-kasus yang memerlukan sinkronisasi .

Untuk menyinkronkan utas dalam program multi-utas, kami akan menggunakan kunci . Ketika akses diperlukan untuk dibatasi hanya dengan satu utas pada satu waktu, kunci mulai bekerja. The kunci implementasi konsep dapat bervariasi dari prosesor ke prosesor. Mari kita cari tahu bagaimana kunci sederhana dapat bekerja dari sudut pandang algoritmik.

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

Algoritma yang diberikan dapat diimplementasikan dalam bahasa perangkat keras sebagai berikut. Kami akan mengasumsikan prosesor tunggal dan menganalisis perilaku kunci di dalamnya. Untuk latihan ini, mari kita asumsikan salah satu prosesor berikut: MIPS , Alpha , ARM atau Power .

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

Program ini tampaknya OK, tetapi sebenarnya tidak. Kode di atas menderita masalah sebelumnya; sinkronisasi . Mari kita temukan masalahnya. Asumsikan nilai awal kunci menjadi nol. Jika dua utas menjalankan kode ini, satu mungkin mencapai SW R1, kunci sebelum yang lain membaca variabel kunci . Jadi, keduanya mengira bahwa kuncinya itu gratis. Untuk mengatasi masalah ini, ada instruksi lain yang disediakan selain LW dan SW sederhana . Ini disebut instruksi Read-Modify-Write . Ini adalah instruksi yang kompleks (terdiri dari sub-instruksi) yang memastikan prosedur akuisisi kunci dilakukan hanya oleh satu orang thread pada suatu waktu. Perbedaan Baca-Ubah-Tulis dibandingkan dengan instruksi Baca dan Tulis sederhana adalah bahwa ia menggunakan cara Memuat dan Menyimpan yang berbeda . Ia menggunakan LL (Load Linked) untuk memuat variabel kunci dan SC (Store Conditional) untuk menulis ke variabel kunci. Link Register tambahan digunakan untuk memastikan prosedur akuisisi kunci dilakukan oleh satu thread. Algoritma diberikan di bawah ini.

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

Saat register tautan disetel ulang, jika utas lain menganggap kunci tersebut bebas, ia tidak akan dapat menulis nilai tambahan ke kunci lagi. Dengan demikian, konkurensi akses ke variabel kunci diperoleh.

Perbedaan inti antara kritis dan atom berasal dari gagasan bahwa:

Mengapa menggunakan kunci (variabel baru) sementara kita dapat menggunakan variabel aktual (yang kita lakukan operasi di atasnya), sebagai variabel kunci?

Menggunakan variabel baru untuk kunci akan mengarah ke bagian kritis , sedangkan menggunakan variabel aktual sebagai kunci akan mengarah ke konsep atom . Bagian kritis berguna ketika kita melakukan banyak perhitungan (lebih dari satu baris) pada variabel aktual. Itu karena, jika hasil perhitungan tersebut gagal dituliskan pada variabel sebenarnya, seluruh prosedur harus diulang untuk menghitung hasilnya. Hal ini dapat menyebabkan kinerja yang buruk dibandingkan menunggu kunci dibuka sebelum memasuki wilayah komputasi tinggi. Dengan demikian, direkomendasikan untuk menggunakan perintah atomic setiap kali Anda ingin melakukan komputasi tunggal (x ++, x--, ++ x, --x, dll.) Dan menggunakandirektif kritis ketika wilayah yang lebih kompleks secara komputasi sedang dilakukan oleh bagian intensif.

hexpheus
sumber
-5

atomic relatif efisien kinerja ketika Anda perlu mengaktifkan pengecualian timbal balik hanya untuk satu instruksi serupa tidak benar tentang omp critical.

Mahesh
sumber
13
Ini tidak lebih dari pernyataan ulang dengan kata-kata yang buruk dari jawaban yang diterima tanpa penjelasan.
Kinerja Tinggi Mark
-5

atomic adalah pernyataan tunggal bagian Kritis, yaitu Anda mengunci untuk satu eksekusi pernyataan

bagian kritis adalah kunci pada blok kode

Kompiler yang baik akan menerjemahkan kode kedua Anda dengan cara yang sama seperti yang pertama

Wissam Y. Khalil
sumber
Itu salah. Tolong jangan membicarakan hal-hal yang tidak Anda mengerti.
jcsahnwaldt Memulihkan Monica