CPU (pengontrol memorinya secara khusus) dapat memanfaatkan fakta bahwa memori tersebut tidak bermutasi
Keuntungannya, fakta ini menyelamatkan kompiler dari menggunakan instruksi membar ketika data diakses.
Sebuah penghalang memori, juga dikenal sebagai membar, pagar memori atau instruksi pagar, adalah jenis instruksi penghalang yang menyebabkan unit pemrosesan pusat (CPU) atau kompiler untuk menegakkan batasan pemesanan pada operasi memori yang dikeluarkan sebelum dan setelah instruksi penghalang. Ini biasanya berarti bahwa operasi tertentu dijamin akan dilakukan sebelum penghalang, dan lainnya setelah itu.
Hambatan memori diperlukan karena sebagian besar CPU modern menggunakan optimasi kinerja yang dapat mengakibatkan eksekusi out-of-order. Penyusunan ulang operasi memori ini (memuat dan menyimpan) biasanya tidak diperhatikan dalam satu utas eksekusi, tetapi dapat menyebabkan perilaku tak terduga dalam program dan driver perangkat bersamaan kecuali jika dikendalikan dengan hati-hati ...
Anda lihat, ketika data diakses dari utas yang berbeda, pada CPU multi-inti, ia berjalan sebagai berikut: utas yang berbeda berjalan pada inti yang berbeda, masing-masing menggunakan cache sendiri (lokal ke intinya) - salinan dari beberapa cache global.
Jika data bisa berubah dan programmer membutuhkannya agar konsisten di antara berbagai utas, tindakan perlu diambil untuk menjamin konsistensi. Untuk programmer, ini berarti menggunakan konstruksi sinkronisasi ketika mereka mengakses (misalnya membaca) data di utas tertentu.
Untuk kompiler, konstruk sinkronisasi dalam kode berarti perlu memasukkan instruksi membar untuk memastikan bahwa perubahan yang dilakukan pada salinan data di salah satu inti diperbanyak dengan baik ("dipublikasikan"), untuk menjamin bahwa cache di inti lainnya memiliki salinan yang sama (terkini).
Menyederhanakan lihat catatan di bawah ini , inilah yang terjadi pada prosesor multi-core untuk membar:
- Semua core berhenti diproses - untuk menghindari penulisan secara tidak sengaja ke cache.
- Semua pembaruan yang dibuat untuk cache lokal ditulis kembali ke global - untuk memastikan bahwa cache global berisi sebagian besar data terbaru. Ini membutuhkan waktu.
- Data yang diperbarui ditulis kembali dari cache global ke cache lokal - untuk memastikan bahwa cache lokal berisi data terbaru. Ini membutuhkan waktu.
- Semua core melanjutkan eksekusi.
Soalnya, semua core tidak melakukan apa-apa saat data sedang disalin bolak-balik antara cache global dan lokal . Ini diperlukan untuk memastikan bahwa data yang dapat diubah dapat disinkronkan dengan benar (aman untuk thread). Jika ada 4 core, semua 4 berhenti dan menunggu sementara cache sedang disinkronkan. Jika ada 8, semua 8 berhenti. Jika ada 16 ... yah Anda punya 15 core melakukan apa-apa sambil menunggu hal-hal yang perlu dilakukan di salah satu dari ini.
Sekarang, mari kita lihat apa yang terjadi ketika data tidak dapat diubah? Apa pun utas yang mengaksesnya, dijamin sama. Untuk programmer, ini berarti tidak perlu memasukkan konstruksi sinkronisasi ketika mereka mengakses (baca) data di utas tertentu.
Untuk kompiler, ini pada gilirannya berarti tidak perlu memasukkan instruksi membar .
Akibatnya, akses ke data tidak perlu menghentikan inti dan menunggu saat data sedang ditulis bolak-balik antara cache global dan lokal. Itu keuntungan dari fakta bahwa memori tidak bermutasi .
Perhatikan penjelasan yang agak menyederhanakan di atas menghilangkan beberapa efek negatif yang lebih rumit dari data yang bisa berubah, misalnya pada pipelining . Untuk menjamin pemesanan yang diperlukan, CPU harus membatalkan pileline yang dipengaruhi oleh perubahan data - itu adalah penalti performa lain. Jika ini diterapkan dengan pembatalan langsung (dan dengan demikian dapat diandalkan :) dari semua pipa, maka efek negatifnya semakin diperkuat.