Dengan cara saya mendekati berbagai hal, jenis multithreading datang gratis dan relatif mudah untuk diterapkan di belakang. Tapi saya berpikir tentang data dulu. Saya tidak tahu apakah ini berfungsi untuk semua domain, tetapi saya akan mencoba untuk membahas bagaimana saya melakukannya.
Jadi pertama-tama ini semua tentang jenis data kasar yang diperlukan untuk perangkat lunak yang akan sering diproses. Jika itu adalah permainan yang mungkin hal-hal seperti jerat, suara, gerakan, penghasil partikel, lampu, tekstur, hal-hal semacam ini. Dan tentu saja ada banyak hal yang perlu dipikirkan jika Anda menelusuri hanya ke jerat dan berpikir tentang bagaimana mereka harus diwakili, tetapi kami akan melewatkan itu untuk saat ini. Saat ini kami sedang berpikir di tingkat arsitektur terluas.
Dan pikiran pertama saya adalah, "Bagaimana kita menyatukan representasi semua hal ini sehingga kita dapat mencapai pola akses yang relatif seragam untuk semua jenis hal ini?" Dan pemikiran pertama saya mungkin untuk menyimpan setiap jenis benda dalam susunannya yang berdekatan dengan cara daftar-tipe gratis untuk mendapatkan kembali ruang kosong. Dan itu cenderung menyatukan API sehingga kita bisa lebih mudah, katakanlah, menggunakan jenis kode yang sama untuk membuat serial jerat seperti yang kita lakukan lampu dan tekstur, setidaknya sejauh di mana dan bagaimana komponen ini diakses. Semakin kita dapat menyatukan bagaimana semua hal direpresentasikan, semakin banyak kode yang mengakses hal-hal tersebut cenderung berbentuk seragam.
Itu keren. Sekarang kita juga dapat menunjuk hal-hal ini dengan indeks 32-bit dan hanya mengambil setengah memori dari pointer 64-bit. Dan hei, kita dapat melakukan set persimpangan dalam waktu linier sekarang jika kita dapat mengaitkan bitset paralel, misalnya Kita juga dapat mengaitkan data ke salah satu dari hal-hal ini dengan sangat murah secara paralel karena kita mengindeks semuanya. Oh dan bitet itu dapat memberikan kita kembali satu set indeks yang diurutkan untuk dilalui secara berurutan untuk pola akses memori yang ditingkatkan, tidak harus memuat ulang garis cache yang sama beberapa kali dalam satu loop tunggal. Kami dapat menguji 64-bit pada suatu waktu. Jika semua 64-bit tidak disetel, kita dapat melewati lebih dari 64 elemen sekaligus. Jika semuanya diatur, kita bisa memproses semuanya sekaligus. Jika beberapa diatur tetapi tidak semua, kita dapat menggunakan instruksi FFS untuk dengan cepat menentukan bit mana yang ditetapkan.
Tapi oh, tunggu, itu agak mahal jika kita hanya ingin mengaitkan data dengan beberapa ratus hal dari puluhan ribu hal. Jadi mari kita gunakan array jarang sebagai gantinya, seperti:
Dan hei, sekarang kita memiliki semua yang tersimpan dalam array jarang dan mengindeksnya, akan sangat mudah untuk membuat ini menjadi struktur data yang persisten.
Sekarang kita dapat menulis lebih banyak fungsi murah tanpa efek samping karena mereka tidak perlu menyalin apa yang belum berubah.
Dan di sini saya sudah diberikan lembar contekan setelah belajar tentang mesin ECS, tetapi sekarang mari kita pikirkan tentang jenis fungsi luas apa yang harus beroperasi pada setiap jenis komponen. Kita bisa menyebutnya "sistem". "SoundSystem" dapat memproses komponen "Sound". Setiap sistem adalah fungsi luas yang beroperasi pada satu atau lebih tipe data.
Itu membuat kami memiliki banyak kasus di mana, untuk setiap jenis komponen tertentu, hanya satu atau dua sistem yang secara umum akan mengaksesnya. Hmm, yang pasti seperti itu akan membantu dengan keamanan benang dan benar-benar membawa pertengkaran thread ke minimum.
Selanjutnya saya mencoba untuk berpikir tentang bagaimana melakukan melewati data yang homogen. Alih-alih seperti:
for each thing:
play with it
cuddle it
kill it
Saya berusaha untuk membaginya menjadi beberapa, lebih sederhana:
for each thing:
play with it
for each thing:
cuddle it
for each thing:
kill it
Itu kadang-kadang membutuhkan menyimpan beberapa keadaan menengah untuk lulus ditangguhkan homogen berikutnya untuk memproses tetapi saya menemukan itu benar-benar membantu saya untuk mempertahankan dan alasan tentang kode, mengetahui bahwa setiap loop memiliki lebih sederhana, logika lebih seragam. Dan hei, sepertinya itu akan menyederhanakan keamanan thread dan mengurangi pertikaian thread.
Dan Anda tetap seperti ini sampai Anda menemukan bahwa Anda memiliki arsitektur yang sangat mudah diparalelkan dengan kepercayaan tentang keamanan dan kebenaran utasnya, tetapi semuanya pada awalnya dengan fokus menyatukan representasi data, memiliki pola akses memori yang lebih dapat diprediksi, mengurangi penggunaan memori, menyederhanakan aliran kontrol ke lintasan yang lebih homogen, mengurangi jumlah fungsi dalam sistem Anda yang menyebabkan efek samping tanpa menimbulkan biaya penyalinan yang sangat mahal, menyatukan API Anda, dll.
Ketika Anda menggabungkan semua hal ini, Anda cenderung berakhir dengan sistem yang meminimalkan jumlah keadaan bersama di mana Anda agak tersandung pada desain yang sangat ramah untuk konkurensi. Dan jika ada negara yang perlu dibagikan, Anda sering menemukan tidak memiliki banyak pertentangan di mana itu murah untuk menggunakan beberapa sinkronisasi tanpa menyebabkan kemacetan lalu lintas thread, dan lebih jauh lagi itu sering dapat ditangani oleh struktur data pusat Anda yang menyatukan representasi semua hal dalam sistem sehingga Anda tidak perlu menerapkan sinkronisasi utas ke seratus tempat yang berbeda, hanya segelintir.
Sekarang ketika kita menelusuri ke salah satu komponen yang lebih kompleks seperti jerat, kita mengulangi proses yang sama untuk mendesainnya, dimulai dengan memikirkan data terlebih dahulu. Dan jika kita melakukan itu dengan benar, kita bahkan mungkin dapat dengan mudah memparalelkan pemrosesan mesh tunggal, tetapi desain arsitektur yang lebih luas yang kami bangun sudah memungkinkan kami untuk memaralelkan pemrosesan beberapa mesh.