Ada beberapa contoh yang bagus di sini, tetapi saya ingin bergabung dengan beberapa contoh pribadi di mana ketidakberdayaan membantu satu ton. Dalam kasus saya, saya mulai merancang struktur data konkuren yang tidak dapat diubah terutama dengan harapan hanya bisa menjalankan kode secara paralel dengan tumpang tindih membaca dan menulis dan tidak harus khawatir tentang kondisi balapan. Ada ceramah yang diberikan John Carmack semacam itu yang mengilhami saya untuk melakukannya di mana ia berbicara tentang gagasan semacam itu. Ini adalah struktur yang cukup mendasar dan cukup sepele untuk diterapkan seperti ini:
Tentu saja dengan beberapa lonceng dan peluit di sana seperti bisa menghilangkan elemen dalam waktu yang konstan dan meninggalkan lubang yang dapat direklamasi di belakang dan membuat balok dihilangkan jika mereka menjadi kosong dan berpotensi dibebaskan untuk contoh abadi yang diberikan. Tetapi pada dasarnya untuk memodifikasi struktur, Anda memodifikasi versi "sementara" dan secara atomis melakukan perubahan yang Anda buat untuk mendapatkan salinan abadi yang tidak menyentuh yang lama, dengan versi baru hanya membuat salinan baru dari blok yang harus dibuat unik saat menyalin dangkal dan referensi menghitung yang lain.
Namun, saya tidak menemukan itu bahwaberguna untuk keperluan multithreading. Bagaimanapun, masih ada masalah konseptual di mana, katakanlah, sistem fisika menerapkan fisika secara bersamaan ketika seorang pemain mencoba untuk memindahkan elemen di dalam dunia. Salinan data transformasi yang tidak berubah yang Anda ikuti, yang ditransformasikan oleh pemain atau yang ditransformasikan oleh sistem fisika? Jadi saya belum benar-benar menemukan solusi yang bagus dan sederhana untuk masalah konseptual dasar ini kecuali untuk memiliki struktur data yang bisa berubah yang hanya mengunci dengan cara yang lebih cerdas dan mencegah tumpang tindih membaca dan menulis ke bagian yang sama dari buffer untuk menghindari kemacetan utas. Itu sesuatu yang tampaknya John Carmack telah menemukan cara untuk menyelesaikannya dalam permainannya; setidaknya dia membicarakannya seperti dia hampir bisa melihat solusi tanpa membuka mobil cacing. Saya belum sampai sejauh dia dalam hal itu. Yang bisa saya lihat adalah pertanyaan desain tanpa akhir jika saya mencoba untuk memaralelkan semua yang ada di sekitar yang tidak berubah. Saya berharap saya bisa menghabiskan satu hari mencerna otaknya karena sebagian besar usaha saya dimulai dengan ide-ide yang dia buang.
Namun demikian, saya menemukan nilai yang sangat besar dari struktur data yang tidak dapat diubah ini di bidang lain. Saya bahkan menggunakannya sekarang untuk menyimpan gambar yang benar-benar aneh dan memang membuat akses acak memerlukan beberapa instruksi lagi (shift kanan dan bitwise and
bersama dengan lapisan tipuan pointer), tapi saya akan membahas manfaat di bawah ini.
Batalkan Sistem
Salah satu tempat paling cepat yang saya temukan untuk mendapat manfaat dari ini adalah sistem undo. Kode sistem undo dulu merupakan salah satu hal yang paling rawan kesalahan di area saya (industri FX visual), dan tidak hanya dalam produk yang saya kerjakan tetapi dalam produk pesaing (sistem undo mereka juga terkelupas) karena ada begitu banyak perbedaan jenis data yang perlu dikhawatirkan akan dibatalkan dan diulang dengan benar (sistem properti, perubahan data mesh, perubahan shader yang tidak berbasis properti seperti bertukar satu dengan yang lain, perubahan hierarki adegan seperti mengubah induk dari anak, perubahan gambar / tekstur, dll. dll.).
Jadi jumlah kode undo yang diperlukan sangat besar, sering menyaingi jumlah kode yang mengimplementasikan sistem yang sistem undo harus mencatat perubahan keadaan. Dengan bersandar pada struktur data ini, saya bisa mengembalikan sistem undo menjadi seperti ini:
on user operation:
copy entire application state to undo entry
perform operation
on undo/redo:
swap application state with undo entry
Biasanya kode di atas akan sangat tidak efisien ketika data adegan Anda membentang gigabytes untuk menyalinnya secara keseluruhan. Tetapi struktur data ini hanya menyalin hal-hal dangkal yang tidak berubah, dan itu sebenarnya membuatnya cukup murah menyimpan salinan seluruh kondisi aplikasi yang tidak berubah. Jadi sekarang saya dapat mengimplementasikan sistem undo semudah kode di atas dan hanya fokus menggunakan struktur data yang tidak berubah ini untuk membuat penyalinan bagian yang tidak berubah dari status aplikasi lebih murah dan lebih murah dan lebih murah. Sejak saya mulai menggunakan struktur data ini, semua proyek pribadi saya memiliki sistem undo hanya menggunakan pola sederhana ini.
Sekarang masih ada beberapa overhead di sini. Terakhir kali saya mengukur sekitar 10 kilobyte hanya untuk menyalin seluruh kondisi aplikasi tanpa membuat perubahan apa pun (ini tidak tergantung pada kompleksitas adegan karena adegan diatur dalam hierarki, jadi jika tidak ada yang di bawah perubahan root, hanya root dangkal disalin tanpa harus turun ke anak-anak). Itu jauh dari 0 byte seperti yang diperlukan untuk sistem undo yang hanya menyimpan delta. Tetapi pada 10 kilobyte membatalkan overhead per operasi, itu masih hanya megabyte per 100 operasi pengguna. Ditambah lagi, saya masih bisa menekannya lebih jauh di masa depan jika diperlukan.
Pengecualian-Keamanan
Pengecualian keamanan dengan aplikasi yang kompleks bukanlah masalah sepele. Namun, ketika status aplikasi Anda tidak berubah dan Anda hanya menggunakan objek sementara untuk mencoba melakukan transaksi perubahan atom, maka itu pada dasarnya pengecualian-aman karena jika ada bagian dari kode yang dilemparkan, transien dibuang sebelum memberikan salinan baru yang tidak dapat diubah . Sehingga meremehkan salah satu hal tersulit yang selalu saya temukan untuk mendapatkan yang benar dalam basis kode C ++ kompleks.
Terlalu banyak orang sering hanya menggunakan sumber daya yang sesuai dengan RAII di C ++ dan berpikir itu sudah cukup untuk menjadi pengecualian-aman. Seringkali tidak, karena suatu fungsi umumnya dapat menyebabkan efek samping untuk menyatakan di luar yang lokal untuk ruang lingkupnya. Anda biasanya harus mulai berurusan dengan pelindung lingkup dan logika rollback canggih dalam kasus tersebut. Struktur data ini membuatnya jadi saya sering tidak perlu repot dengan itu karena fungsinya tidak menyebabkan efek samping. Mereka mengembalikan salinan status aplikasi yang tidak berubah dan bukannya mengubah status aplikasi.
Pengeditan Tidak Merusak
Pengeditan non-destruktif pada dasarnya adalah operasi layering / stacking / menghubungkan bersama tanpa menyentuh data pengguna asli (hanya input data dan data output tanpa menyentuh input). Biasanya sepele untuk diterapkan dengan aplikasi gambar sederhana seperti Photoshop dan mungkin tidak mendapat manfaat sebanyak itu dari struktur data ini karena banyak operasi mungkin hanya ingin mengubah setiap piksel dari keseluruhan gambar.
Namun, dengan editing mesh non-destruktif, misalnya, banyak operasi sering ingin mengubah hanya sebagian dari mesh. Satu operasi mungkin hanya ingin memindahkan beberapa simpul di sini. Yang lain mungkin hanya ingin membagi beberapa poligon di sana. Di sini struktur data yang tidak berubah membantu satu ton dalam menghindari kebutuhan untuk harus membuat seluruh salinan seluruh mesh hanya untuk mengembalikan versi baru dari mesh dengan sebagian kecil dari itu berubah.
Meminimalkan Efek Samping
Dengan struktur ini di tangan, itu juga membuatnya mudah untuk menulis fungsi yang meminimalkan efek samping tanpa menimbulkan penalti kinerja yang besar untuk itu. Saya mendapati diri saya semakin banyak menulis fungsi yang hanya mengembalikan seluruh struktur data yang tidak dapat diubah dengan nilai hari ini tanpa menimbulkan efek samping, bahkan ketika itu tampak agak boros.
Sebagai contoh, biasanya godaan untuk mengubah banyak posisi mungkin untuk menerima matriks dan daftar objek dan mengubah mereka dengan cara yang bisa berubah. Hari-hari ini saya menemukan diri saya baru saja mengembalikan daftar objek baru.
Ketika Anda memiliki lebih banyak fungsi seperti ini di sistem Anda yang tidak menimbulkan efek samping, itu pasti membuatnya lebih mudah untuk mempertimbangkan kebenarannya serta menguji kebenarannya.
Manfaat Salinan Murah
Jadi bagaimanapun, ini adalah area di mana saya menemukan yang paling banyak digunakan dari struktur data yang tidak dapat diubah (atau struktur data yang persisten). Saya juga agak terlalu bersemangat pada awalnya dan membuat pohon yang tidak bisa diubah dan daftar tertaut yang tidak berubah dan tabel hash yang tidak dapat berubah, tetapi seiring waktu saya jarang menemukan banyak kegunaan untuk itu. Saya terutama menemukan sebagian besar penggunaan array mirip chunky yang tidak bisa diubah dalam diagram di atas.
Saya juga masih memiliki banyak kode yang dapat digunakan dengan mutables (merasa ini adalah kebutuhan praktis setidaknya untuk kode tingkat rendah), tetapi status aplikasi utama adalah hierarki yang tidak dapat diubah, menelusuri dari adegan yang tidak dapat diubah ke komponen yang tidak dapat diubah di dalamnya. Beberapa komponen yang lebih murah masih disalin secara penuh, tetapi yang paling mahal seperti jerat dan gambar menggunakan struktur yang tidak dapat diubah untuk memungkinkan salinan sebagian murah itu hanya dari bagian-bagian yang perlu diubah.
ConcurrentModificationException
yang biasanya disebabkan oleh utas yang sama memutasikan koleksi di utas yang sama, di badanforeach
loop di atas koleksi yang sama.