Kinerja kode ADT berorientasi tugas tunggal pada CPU modern

32

Bekerja dalam data yang tidak dapat diubah dengan penugasan tunggal memiliki efek yang jelas membutuhkan lebih banyak memori, orang akan mengira, karena Anda terus-menerus menciptakan nilai - nilai baru (meskipun kompiler di bawah selimut melakukan trik penunjuk untuk menjadikan ini lebih sedikit masalah).

Tapi saya sudah mendengar beberapa kali sekarang bahwa kerugian di sana dalam kinerja lebih besar daripada keuntungan dalam cara bahwa CPU (pengontrol memorinya khusus) dapat mengambil keuntungan dari kenyataan bahwa memori tidak bermutasi (sebanyak).

Saya berharap seseorang dapat menjelaskan bagaimana ini benar (atau jika tidak?).

Dalam komentar di posting lain disebutkan bahwa Abstrak Jenis Data (ADT) ada hubungannya dengan ini yang membuat saya semakin penasaran, bagaimana ADT secara khusus mempengaruhi cara CPU berurusan dengan memori? Namun ini adalah samping, sebagian besar saya hanya tertarik pada bagaimana kemurnian bahasa tentu mempengaruhi kinerja CPU dan cache dll.

Jimmy Hoffa
sumber
2
ini sebagian besar berguna dalam multithreading, di mana pembaca secara atom dapat mengambil snapshot dan aman dalam pengetahuan bahwa itu tidak akan bermutasi saat dia membacanya
ratchet freak
@ scratchetfreak Saya mendapatkan dari sudut pandang pemrograman bahwa kode Anda mendapatkan lebih banyak jaminan keamanan, tetapi rasa ingin tahu saya adalah tentang pengontrol memori pada CPU dan bagaimana perilaku ini penting baginya (atau jika tidak) karena saya pernah mendengar klaim bandied tentang tangan penuh waktu yang mengatakan itu lebih efisien untuk pengontrol memori, dan saya tidak tahu detail level rendah cukup baik untuk mengatakan apakah atau bagaimana ini mungkin benar.
Jimmy Hoffa
Bahkan jika itu benar, saya tidak akan berpikir bahwa kurang modifikasi memori adalah nilai jual terbaik untuk ketetapan. Memori ada untuk dimodifikasi, setelah semua, dan CPU dan manajer memori sudah cukup bagus selama bertahun-tahun.
Rein Henrichs
1
Saya juga ingin menunjukkan bahwa efisiensi memori tidak harus bergantung pada optimisasi kompiler ketika menggunakan struktur yang tidak dapat diubah. Dalam contoh ini let a = [1,2,3] in let b = 0:a in (a, b, (-1):c)berbagi mengurangi kebutuhan memori, tetapi tergantung pada definisi (:)dan []dan tidak compiler. Kupikir? Tidak yakin tentang yang ini.

Jawaban:

28

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:

  1. Semua core berhenti diproses - untuk menghindari penulisan secara tidak sengaja ke cache.
  2. Semua pembaruan yang dibuat untuk cache lokal ditulis kembali ke global - untuk memastikan bahwa cache global berisi sebagian besar data terbaru. Ini membutuhkan waktu.
  3. Data yang diperbarui ditulis kembali dari cache global ke cache lokal - untuk memastikan bahwa cache lokal berisi data terbaru. Ini membutuhkan waktu.
  4. 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.

agas
sumber