Operasi membandingkan dan bertukar multi-kata yang praktis

10

Dalam makalah dengan judul yang sama dengan pertanyaan ini, penulis menggambarkan bagaimana membangun operasi CAS multi-kata linearizable nonblocking hanya menggunakan CAS kata tunggal. Mereka pertama kali memperkenalkan operasi penukaran ganda-bandingkan-tunggal - RDCSS, sebagai berikut:

word_t RDCSS(RDCSSDescriptor_t *d) {
  do {
    r = CAS1(d->a2, d->o2, d);
    if (IsDescriptor(r)) Complete(r);
  } while (IsDescriptor(r));
  if (r == d->o2) Complete(d); // !!
  return r;
}

void Complete(RDCSSDescriptor_t *d) {
  v = *(d->a1);
  if (v == d->o1) CAS1(d->a2, d, d->n2);
  else CAS1(d->a2, d, d->o2);
}

di mana RDCSSDescriptor_tadalah struktur dengan bidang-bidang berikut:

  • a1 - alamat kondisi pertama
  • o1 - nilai yang diharapkan di alamat pertama
  • a2 - alamat kondisi kedua
  • o2 - nilai yang diharapkan di alamat kedua
  • n2 - nilai baru yang akan ditulis di alamat kedua

Deskriptor ini dibuat dan diinisialisasi sekali dalam utas yang memulai operasi RDCSS - tidak ada utas lain yang memiliki referensi sampai CAS1 pertama dalam fungsi RDCSSberhasil, membuat deskriptor dapat dijangkau (atau aktif dalam terminologi makalah ini).

Gagasan di balik algoritma adalah sebagai berikut - ganti lokasi memori kedua dengan deskriptor yang mengatakan apa yang ingin Anda lakukan. Kemudian, mengingat bahwa deskriptor hadir, periksa lokasi memori pertama untuk melihat apakah nilainya berubah. Jika belum, ganti deskriptor di lokasi memori kedua dengan nilai baru. Jika tidak, atur lokasi memori kedua kembali ke nilai lama.

Para penulis tidak menjelaskan mengapa garis dengan !!komentar itu perlu dalam makalah. Tampak bagi saya bahwa CAS1instruksi dalam Completefungsi akan selalu gagal setelah pemeriksaan ini, asalkan tidak ada modifikasi bersamaan. Dan jika ada modifikasi bersamaan antara cek dan CAS di Complete, maka utas yang melakukan pemeriksaan harus tetap gagal dengan CAS-nya Complete, karena modifikasi bersamaan tidak boleh menggunakan deskriptor yang sama d.

Pertanyaan saya adalah: Dapatkah pemeriksaan dalam fungsi RDCSSS, if (r == d->o2)...dihilangkan, dengan RDCSS masih mempertahankan semantik dari instruksi pembandingan ganda, pertukaran tunggal yang linierisasi dan bebas kunci ? (baris dengan !!komentar)

Jika tidak, dapatkah Anda menggambarkan skenario di mana garis ini sebenarnya diperlukan untuk memastikan kebenaran?

Terima kasih.

axel22
sumber
Pertama, untuk memahami apa yang terjadi, kita perlu melihat struktur data RDCSSDescriptor_t. Kedua, ini mungkin di luar topik karena tidak membahas ilmu komputer teoretis; akan lebih baik untuk menanyakan ini di stackoverflow.com.
Dave Clarke
Tautan ke kertas terputus.
Aaron Sterling
1
Saya minta maaf atas tautannya - sekarang seharusnya berfungsi. Saya telah memperbarui pertanyaan untuk menjelaskan apa deskripsi itu. Alasan saya belum memposting ini di stackoverflow.com adalah karena FAQ mengatakan bahwa situs ini untuk pertanyaan tingkat penelitian dalam ilmu komputer. Saya pikir pertanyaan tentang kebebasan kunci dan kelurusan linier dari suatu algoritma memenuhi syarat. Saya harap saya salah memahami FAQ.
axel22
Kata kunci yang Anda lewatkan dalam FAQ adalah "teoretis". Karena beberapa orang menganggap pertanyaan itu menarik, saya akan membiarkannya terbuka.
Dave Clarke
3
@ Dave: Saya bukan ahli dalam sub-area ini, tetapi bagi saya ini terdengar seperti pertanyaan TCS yang sangat khas. Anda diberi dua model perhitungan (A: dengan CAS kata tunggal, B: dengan CAS multi-kata) dan ukuran kerumitan (jumlah CAS), dan Anda ditanya apakah Anda dapat mensimulasikan model B dalam model A, dan dengan overhead terburuk apa. (Ini mungkin agak menyesatkan bahwa simulasi diberikan sebagai bagian dari kode C daripada pseudocode; ini mungkin menyarankan kepada orang teori bahwa ini terkait dengan tantangan pemrograman dunia nyata.)
Jukka Suomela

Jawaban:

9

Dalam lingkungan runtime bersamaan hal-hal sederhana bisa terasa aneh ... harap ini bisa membantu.

Kami memiliki BUILT-IN ATOMIC CAS1 yang memiliki semantik ini:

int CAS1(int *addr, int oldval, int newval) {
  int currval = *addr;
  if (currval == oldval) *addr = newval;
  return currval;
}

Kita perlu mendefinisikan fungsi RDCSS ATOMIC menggunakan CAS1 dan memiliki semantik berikut:

int RDCSS(int *addr1, int oldval1, int *addr2, int oldval2, int newval2) {
  int res = *addr;
  if (res == oldval2 && *addr1 == oldval1) *addr2 = newval2;
  return res;
}

Secara intuitif: kita perlu mengubah nilai pada addr2 secara kontinyu hanya jika * addr1 == oldval1 ... jika utas lain mengubahnya, kita dapat membantu utas lainnya menyelesaikan operasi, maka kita bisa mencoba lagi.

Fungsi RDCSS akan digunakan (lihat artikel) untuk mendefinisikan CASN. Sekarang, kita mendefinisikan deskriptor RDCSS dengan cara berikut:

RDCSSDESCRI
int *addr1   
int oldval1
int *addr2   
int oldval2
int newval2

Kemudian kami mengimplementasikan RDCSS dengan cara berikut:

int RDCSS( RDCSSDESCRI *d ) {
  do {
    res = CAS1(d->addr2, d->oldval2, d);  // STEP1
    if (IsDescriptor(res)) Complete(res); // STEP2
  } while (IsDescriptor(res);             // STEP3
  if (res == d->oldval2) Complete(d);     // STEP4
  return res;
}

void Complete( RDCSSDESCRI *d ) {
  int val = *(d->addr1);
  if (val == d->oldval1) CAS1(d->addr2, d, d->newval2);
    else CAS1(d->addr2, d, d->oldval2);  
}
  • LANGKAH1: pertama kita mencoba mengubah nilai * addr2 ke deskriptor d kita (milik), jika CAS1 berhasil maka res == d-> oldval2 (mis. Res BUKAN deskriptor)
  • STEP2: periksa apakah res adalah deskriptor yaitu STEP1 gagal (utas lain diubah addr2) ... bantu utas lain menyelesaikan operasi
  • STEP3: coba lagi STEP1 jika kami tidak berhasil menyimpan deskriptor kami d
  • STEP4: jika kami mengambil nilai yang kami harapkan dari addr2 maka kami berhasil menyimpan deskriptor (pointer) kami di addr2 dan kami dapat menyelesaikan tugas kami menyimpan newval2 ke * addr2 iif * addr1 == oldval1

JAWABAN UNTUK PERTANYAAN ANDA

Jika kita menghilangkan STEP4 maka if (... && * addr1 == oldval1) * addr2 = newval2 bagian dari semantic RDCSS tidak akan pernah dieksekusi (... atau lebih baik: itu dapat dieksekusi dengan cara yang tidak dapat diramalkan oleh utas lain yang membantu yang sekarang).

Seperti yang Anda tunjukkan dalam komentar Anda, kondisi jika (res == d1-> oldval2) di STEP4 tidak diperlukan: bahkan jika kami menghilangkannya, kedua CAS1 dalam Complete () akan gagal karena * (d-> addr2)! = D . Tujuan utamanya adalah menghindari panggilan fungsi.

Contoh T1 = thread1, T2 = thread2:

remember that addr1 / addr2 are in a shared data zone !!!

T1 enter RDCSS function
T2 enter RDCSS function
T2 complete STEP1 (and store the pointer to its descriptor d2 in addr2)
T1 at STEP1 the CAS1 fails and res = d2
T2 or T1 completes *(d2->addr2)=d2->newval2 (suppose that *(d2->addr1)==d2->oldval1)
T1 execute STEP1 and now CAS1 can fail because *addr2 == d2->newval2
   and maybe d2->newval2 != d1->oldval2, in every case at the end 
   res == d2->newval2 (fail) or
   res == d1->oldval2 (success)
T1 at STEP2 skips the call to Complete() (because now res is not a descriptor)
T1 at STEP3 exits the loop (because now res is not a descriptor)
T1 at STEP4 T1 is ready to store d1->newval2 to addr2, but only if
   *(d1->addr2)==d (we are working on our descriptor) and *(d1->addr1)==d1->oldval1
   ( Custom() function)
Marzio De Biasi
sumber
Terima kasih, penjelasan yang bagus. Saya benar-benar merindukan titik bahwa CAS1 mengembalikan nilai lama, bukan yang baru.
axel22
Tetapi, dalam skenario, 2 baris terakhir mengatakan bahwa: tanpa kondisi di STEP4, T1 dapat menyimpan nilai, karena addr2mengandung d2->newval2. Tapi, bagi saya sepertinya CAS1 di dalam Completehanya akan gagal, karena mengharapkan nilai lama menjadi deskriptor d1- tidak ada yang akan ditulis oleh T1. Baik?
axel22
@ axel22: Saya melewatkan CAS1 di Lengkap () :-D. Ya Anda benar ... contoh saya salah, kondisi if hanya digunakan untuk menghindari panggilan fungsi, jika kami membuang if () tidak ada yang berubah. Jelas bahwa Lengkap (d) di STEP4 diperlukan. Sekarang saya memodifikasi contohnya.
Marzio De Biasi
Menghindari CAS yang kami harapkan akan gagal adalah teknik optimisasi cache sejauh yang saya tahu, karena pada perangkat keras nyata biasanya memiliki efek negatif seperti pembilasan garis cache dan memperoleh akses eksklusif ke garis cache. Saya kira penulis makalah ingin algoritma menjadi sepraktis mungkin selain benar.
Tim Seguine