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.
sumber
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:
Keuntungannya, fakta ini menyelamatkan kompiler dari menggunakan instruksi membar ketika data diakses.
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:
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.
sumber