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++;
?
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++;
?
Jawaban:
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.
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.
Bagian penting:
Dapat diperluas untuk membuat serial blok dengan penggunaan tag "nama" yang tepat.
Lebih lambat!
Operasi atom:
Jauh lebih cepat!
Hanya memastikan serialisasi operasi tertentu.
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.
Keterbatasan atomic
itu 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:
x
binop =expr
x++
++x
x--
--x
Dalam ekspresi sebelumnya:
x
adalahlvalue
ekspresi dengan tipe skalar.expr
adalah ekspresi dengan tipe skalar, dan tidak mereferensikan objek yang ditunjukx
. binop tidak operator overload dan merupakan salah satu dari+
,*
,-
,/
,&
,^
,|
,<<
, atau>>
.
Saya merekomendasikan untuk menggunakan atomic
bila Anda bisa dan menamai bagian kritis sebaliknya. Memberi nama mereka penting; Anda akan menghindari sakit kepala debugging dengan cara ini.
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.
atomic relatif efisien kinerja ketika Anda perlu mengaktifkan pengecualian timbal balik hanya untuk satu instruksi serupa tidak benar tentang omp critical.
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
++
dan*=
) dan bahwa jika mereka tidak didukung dalam perangkat keras, mereka mungkin akan digantikan olehcritical
bagian.