Saya ingin tahu hal yang sama, jadi saya mengukurnya. Di komputer saya (AMD FX (tm) -8150 Prosesor Delapan-Inti pada 3,612361 GHz), mengunci dan membuka kunci sebuah mutex yang tidak terkunci yang berada dalam jalur cache sendiri dan sudah di-cache, membutuhkan 47 jam (13 ns).
Karena sinkronisasi antara dua inti (saya menggunakan CPU # 0 dan # 1), saya hanya bisa memanggil pasangan kunci / buka sekali setiap 102 n pada dua utas, jadi sekali setiap 51 n, dari mana orang dapat menyimpulkan bahwa dibutuhkan sekitar 38 untuk memulihkan setelah utas melakukan pembukaan kunci sebelum utas berikutnya dapat menguncinya lagi.
Program yang saya gunakan untuk menyelidiki ini dapat ditemukan di sini:
https://github.com/CarloWood/ai-statefultask-testsuite/blob/b69b112e2e91d35b56a39f41809d3e3de2f9e4b8/src/mutex_test.cxx
Perhatikan bahwa ia memiliki beberapa nilai hardcoded khusus untuk kotak saya (xrange, yrange dan overhead rdtsc), jadi Anda mungkin harus bereksperimen dengannya sebelum itu bekerja untuk Anda.
Grafik yang dihasilkannya dalam keadaan itu adalah:
Ini menunjukkan hasil benchmark berjalan pada kode berikut:
uint64_t do_Ndec(int thread, int loop_count)
{
uint64_t start;
uint64_t end;
int __d0;
asm volatile ("rdtsc\n\tshl $32, %%rdx\n\tor %%rdx, %0" : "=a" (start) : : "%rdx");
mutex.lock();
mutex.unlock();
asm volatile ("rdtsc\n\tshl $32, %%rdx\n\tor %%rdx, %0" : "=a" (end) : : "%rdx");
asm volatile ("\n1:\n\tdecl %%ecx\n\tjnz 1b" : "=c" (__d0) : "c" (loop_count - thread) : "cc");
return end - start;
}
Dua panggilan rdtsc mengukur jumlah jam yang diperlukan untuk mengunci dan membuka `mutex '(dengan overhead 39 jam untuk panggilan rdtsc di kotak saya). ASM ketiga adalah loop penundaan. Ukuran loop tunda 1 hitungan lebih kecil untuk utas 1 daripada utas 0, jadi utas 1 sedikit lebih cepat.
Fungsi di atas disebut dalam loop ketat ukuran 100.000. Meskipun demikian fungsinya sedikit lebih cepat untuk utas 1, kedua loop menyinkronkan karena panggilan ke mutex. Ini terlihat dalam grafik dari fakta bahwa jumlah jam yang diukur untuk pasangan kunci / buka sedikit lebih besar untuk ulir 1, untuk memperhitungkan keterlambatan yang lebih pendek dalam loop di bawahnya.
Dalam grafik di atas, titik kanan bawah adalah pengukuran dengan loop_count penundaan 150, dan kemudian mengikuti titik-titik di bawah, ke kiri, loop_count dikurangi dengan satu pengukuran masing-masing. Ketika menjadi 77 fungsi dipanggil setiap 102 ns di kedua utas. Jika kemudian loop_count dikurangi lebih jauh, maka tidak mungkin lagi mensinkronkan utas dan mutex mulai benar-benar terkunci sebagian besar waktu, menghasilkan peningkatan jumlah jam yang diperlukan untuk melakukan kunci / membuka kunci. Juga waktu rata-rata panggilan fungsi meningkat karena ini; jadi titik plot sekarang naik dan ke kanan lagi.
Dari sini kita dapat menyimpulkan bahwa mengunci dan membuka kunci mutex setiap 50 ns bukanlah masalah pada kotak saya.
Kesimpulan saya adalah bahwa jawaban untuk pertanyaan OP adalah bahwa menambahkan lebih banyak mutex lebih baik selama itu menghasilkan lebih sedikit pertengkaran.
Cobalah untuk mengunci mutex sesingkat mungkin. Satu-satunya alasan untuk menempatkan mereka -say- di luar loop adalah jika loop itu loop lebih cepat dari sekali setiap 100 ns (atau lebih tepatnya, jumlah utas yang ingin menjalankan loop itu pada waktu yang sama kali 50 ns) atau ketika 13 ns kali ukuran lingkaran lebih banyak keterlambatan daripada penundaan yang Anda dapatkan dengan pertikaian.
EDIT: Saya mendapat lebih banyak pengetahuan tentang masalah ini sekarang dan mulai meragukan kesimpulan yang saya sampaikan di sini. Pertama-tama, CPU 0 dan 1 berubah menjadi hyper-threaded; meskipun AMD mengklaim memiliki 8 core nyata, pasti ada sesuatu yang sangat mencurigakan karena penundaan antara dua core lainnya jauh lebih besar (yaitu, 0 dan 1 membentuk pasangan, seperti halnya 2 dan 3, 4 dan 5, dan 6 dan 7 ). Kedua, std :: mutex diimplementasikan dengan cara memutar kunci sedikit sebelum benar-benar melakukan panggilan sistem ketika gagal untuk segera mendapatkan kunci pada mutex (yang tidak diragukan lagi akan sangat lambat). Jadi apa yang saya ukur di sini adalah situasi yang paling ideal dan dalam praktiknya, mengunci dan membuka kunci mungkin memerlukan waktu lebih drastis per kunci / membuka kunci.
Intinya, mutex diimplementasikan dengan atom. Untuk menyinkronkan atom antar core, bus internal harus dikunci yang membekukan jalur cache yang sesuai untuk beberapa ratus siklus clock. Dalam hal kunci tidak dapat diperoleh, panggilan sistem harus dilakukan untuk membuat utas tertidur; itu jelas sangat lambat (system calls dalam urutan 10 mircoseconds). Biasanya itu bukan masalah karena utas itu harus tidur - tapi itu bisa menjadi masalah dengan pertengkaran tinggi di mana utas tidak dapat memperoleh kunci untuk waktu yang biasanya berputar dan begitu pula sistem panggilan, tetapi BISA ambil kunci sesaat setelahnya. Misalnya, jika beberapa utas mengunci dan membuka kunci mutex dalam satu lingkaran ketat dan masing-masing menjaga kunci selama 1 mikrodetik atau lebih, maka mereka mungkin diperlambat secara luar biasa oleh fakta bahwa mereka terus-menerus ditidurkan dan dibangunkan lagi. Juga, sekali utas tidur dan utas lain harus membangunkannya, utas itu harus melakukan panggilan sistem dan ditunda ~ 10 mikrodetik; penundaan ini terjadi saat membuka kunci mutex ketika utas lain menunggu mutex itu di kernel (setelah berputar terlalu lama).