* Sejauh yang saya tahu, Unity3D untuk iOS didasarkan pada runtime Mono dan Mono hanya memiliki tanda generasi & sapuan GC.
Sistem GC ini tidak dapat menghindari waktu GC yang menghentikan sistem game. Penggabungan instance dapat mengurangi ini tetapi tidak sepenuhnya, karena kami tidak dapat mengontrol instantiasi yang terjadi di pustaka kelas dasar CLR. Contoh kecil dan sering disembunyikan akan meningkatkan waktu GC akhirnya tidak menentukan. Memaksa GC yang lengkap secara berkala akan sangat menurunkan kinerja (dapatkah Mono memaksa GC yang lengkap, sebenarnya?)
Jadi, bagaimana saya bisa menghindari waktu GC ini ketika menggunakan Unity3D tanpa menurunkan kinerja besar?
unity
performance
Eonil
sumber
sumber
Jawaban:
Untungnya, seperti yang Anda tunjukkan, build COMPACT Mono menggunakan GC generasi (sangat kontras dengan yang Microsoft, seperti WinMo / WinPhone / XBox, yang hanya mempertahankan daftar datar).
Jika gim Anda sederhana, GC harus menanganinya dengan baik, tetapi berikut adalah beberapa petunjuk yang mungkin ingin Anda perhatikan.
Optimalisasi Dini
Pertama, pastikan ini benar-benar masalah bagi Anda sebelum mencoba memperbaikinya.
Jenis referensi mahal Pooling
Anda harus mengumpulkan jenis referensi yang sering Anda buat, atau yang memiliki struktur dalam. Contoh masing-masing adalah:
Bullet
Objek dalam permainan peluru-neraka .Anda harus menggunakan
Stack
sebagai kumpulan Anda (tidak seperti kebanyakan implementasi yang menggunakan aQueue
). Alasan untuk ini adalah karena dengan aStack
jika Anda mengembalikan objek ke kolam dan sesuatu yang lain segera meraihnya; itu akan memiliki peluang yang jauh lebih tinggi untuk berada di halaman aktif - atau bahkan di cache CPU jika Anda beruntung. Hanya saja itu sedikit lebih cepat. Selain itu selalu ukuran batas kolam Anda (abaikan saja 'checkin' jika batas Anda telah terlampaui).Hindari Membuat Daftar Baru untuk Membersihkan Mereka
Jangan membuat yang baru
List
ketika Anda benar-benar bermaksud untukClear()
itu. Anda dapat menggunakan kembali array backend dan menyimpan banyak alokasi dan salinan array. Demikian pula dengan percobaan ini dan buat daftar dengan kapasitas awal yang bermakna (ingat, ini bukan batas - hanya kapasitas awal) - tidak perlu akurat, hanya perkiraan. Ini pada dasarnya berlaku untuk semua jenis koleksi - kecuali untuk aLinkedList
.Gunakan Struct Arrays (atau Daftar) Di Mana Mungkin
Anda mendapatkan sedikit manfaat dari penggunaan struct (atau tipe nilai secara umum) jika Anda membagikannya di antara objek. Sebagai contoh, dalam kebanyakan sistem partikel 'baik', partikel individu disimpan dalam array besar: array dan dan indeks dilewatkan di sekitar bukan partikel itu sendiri. Alasan ini bekerja sangat baik adalah karena ketika GC perlu mengumpulkan array, ia dapat melewatkan konten sepenuhnya (itu array primitif - tidak ada hubungannya di sini). Jadi alih-alih melihat 10.000 objek, GC hanya perlu melihat 1 array: untung besar! Sekali lagi, ini hanya akan bekerja dengan tipe nilai .
Setelah RoyT. memberikan beberapa umpan balik yang layak dan konstruktif yang saya rasa perlu saya kembangkan lebih lanjut. Anda hanya harus menggunakan teknik ini ketika Anda berurusan dengan entitas dalam jumlah besar (ribuan hingga puluhan ribu). Selain itu, harus berupa struct yang tidak boleh memiliki bidang jenis referensi dan harus hidup dalam array yang diketik secara eksplisit. Bertentangan dengan umpan baliknya, kami menempatkannya dalam array yang sangat mungkin merupakan bidang dalam kelas - yang berarti bahwa itu akan mendapatkan heap (kami tidak berusaha untuk menghindari alokasi tumpukan - hanya menghindari pekerjaan GC). Kami benar-benar peduli adalah kenyataan bahwa itu adalah sepotong memori yang berdekatan dengan banyak nilai yang dapat dilihat dengan mudah oleh GC dalam suatu
O(1)
operasi alih-alihO(n)
operasi.Anda juga harus mengalokasikan array ini sedekat mungkin dengan startup aplikasi Anda untuk mengurangi kemungkinan terjadinya fragmentasi , atau kerja berlebihan ketika GC mencoba untuk memindahkan potongan-potongan ini, (dan pertimbangkan untuk menggunakan daftar tautan hybrid alih-alih
List
jenis bawaan. ).GC.Collect ()
Ini jelas merupakan cara TERBAIK untuk menembak diri sendiri (lihat: "Pertimbangan Kinerja") dengan GC generasi. Anda hanya boleh menyebutnya ketika Anda telah membuat jumlah EXTREME sampah - dan satu contoh di mana itu bisa menjadi masalah adalah hanya setelah Anda memuat konten untuk level - dan bahkan kemudian Anda mungkin hanya perlu mengumpulkan generasi pertama (
GC.Collect(0);
) mudah-mudahan mencegah mempromosikan objek ke generasi ketiga.IDisposable dan Field Nulling
Berguna untuk membatalkan bidang ketika Anda tidak lagi membutuhkan objek (lebih-lebih pada objek terbatas). Alasannya adalah dalam rincian tentang cara kerja GC: hanya menghapus objek yang tidak di-rooting (yaitu direferensikan) bahkan jika objek itu akan dicabut karena objek lain yang dihapus dalam koleksi saat ini ( catatan: ini tergantung pada GC rasa digunakan - beberapa benar-benar membersihkan rantai). Selain itu, jika suatu objek bertahan koleksi, itu segera dipromosikan ke generasi berikutnya - ini berarti bahwa setiap benda yang tertinggal di ladang akan dipromosikan selama koleksi. Setiap generasi berturut-turut secara eksponensial lebih mahal untuk dikumpulkan (dan jarang terjadi).
Ambil contoh berikut:
Jika
MyFurtherNestedObject
terdapat objek multi-megabyte, Anda dapat dijamin bahwa GC tidak akan melihatnya cukup lama - karena Anda secara tidak sengaja mempromosikannya ke G3. Bandingkan dengan contoh ini:Pola Disposer membantu Anda mengatur cara yang dapat diprediksi untuk meminta objek untuk menghapus bidang pribadi mereka. Sebagai contoh:
sumber
Instansiasi dinamis sangat sedikit terjadi secara otomatis di dalam pangkalan perpustakaan, kecuali jika Anda memanggil sesuatu yang memerlukannya. Sebagian besar alokasi dan alokasi memori berasal dari kode Anda sendiri dan Anda dapat mengendalikannya.
Seperti yang saya pahami, Anda tidak dapat menghindari ini sepenuhnya - yang dapat Anda lakukan adalah memastikan bahwa Anda mendaur ulang dan menyatukan objek jika memungkinkan, Gunakan struct sebagai pengganti kelas, hindari sistem UnityGUI, dan umumnya hindari membuat objek baru dalam memori dinamis selama waktu ketika kinerja penting.
Anda juga dapat memaksa pengumpulan sampah pada waktu-waktu tertentu, yang mungkin atau mungkin tidak membantu: lihat beberapa saran di sini: http://unity3d.com/support/documentation/Manual/iphone-Optimizing-Scripts.html
sumber
GC.Collect()
= bebaskan memori sekarang tetapi kemungkinan besar masalah nanti.