Selama pembicaraan Persatuan tentang kinerja, orang itu mengatakan kepada kami untuk menghindari for each
loop untuk meningkatkan kinerja dan menggunakan normal untuk loop sebagai gantinya. Apa perbedaan antara for
dan for each
dalam sudut pandang implementasi? Apa yang membuat for each
tidak efisien?
unity
performance
Emad
sumber
sumber
for each
loop berulang di atas apa pun selain array menghasilkan sampah (versi Unity sebelum 5.5)Jawaban:
Loop foreach seolah-olah menciptakan objek IEnumerator dari koleksi yang Anda lewati, dan kemudian berjalan di atasnya. Jadi satu loop seperti ini:
diterjemahkan menjadi sesuatu yang sedikit seperti ini:
(menghilangkan beberapa kerumitan tentang bagaimana enumerator dibuang nanti)
Jika ini dikompilasi secara naif / langsung, maka objek enumerator itu akan dialokasikan pada heap (yang membutuhkan sedikit waktu), bahkan jika tipe dasarnya adalah struct - sesuatu yang disebut tinju. Setelah pengulangan selesai, alokasi ini menjadi sampah untuk dibersihkan nanti, dan permainan Anda dapat gagap sesaat jika cukup banyak sampah yang menumpuk bagi kolektor untuk menjalankan sapuan penuh. Terakhir,
MoveNext
metode danCurrent
properti bisa melibatkan lebih banyak langkah-langkah pemanggilan fungsi daripada hanya mengambil item secara langsungcollection[i]
, jika kompiler tidak sejalan dengan tindakan itu.Unity docs tautan Hellium di atas menunjukkan bahwa bentuk naif ini adalah persis apa yang terjadi di Unity sebelum versi 5.5 , jika mengulangi apa pun selain dari Array asli.
Dalam versi 5.6+ (termasuk versi 2017), tinju yang tidak perlu ini hilang , dan Anda seharusnya tidak mengalami alokasi yang tidak perlu jika Anda menggunakan jenis beton apa pun (mis.
int[]
AtauList<GameObject>
atauQueue<T>
).Anda masih akan mendapatkan alokasi jika metode yang dimaksud hanya tahu bahwa itu bekerja dengan semacam IList atau antarmuka lainnya - dalam kasus itu tidak tahu enumerator spesifik apa yang bekerja dengannya sehingga harus mengotakkannya menjadi objek IEnumerator terlebih dahulu .
Jadi, ada kemungkinan bagus ini saran lama yang tidak begitu penting dalam pengembangan Persatuan modern. Persatuan devs seperti kita bisa sedikit percaya pada hal-hal yang dulu lambat / berbulu dalam versi lama, jadi ikuti saran dari bentuk itu dengan sebutir garam. ;) Mesin berkembang lebih cepat daripada keyakinan kami tentang hal itu.
Dalam praktiknya, saya tidak pernah memiliki permainan yang menunjukkan kelambatan yang terlihat atau cegukan pengumpulan sampah karena menggunakan foreach loop, tetapi jarak tempuh Anda mungkin berbeda. Profil game Anda pada perangkat keras yang Anda pilih untuk melihat apakah itu layak untuk dikhawatirkan. Jika Anda perlu, itu cukup sepele untuk mengganti foreach loop dengan eksplisit untuk loop nanti dalam pengembangan harus manifes masalah, jadi saya tidak akan mengorbankan keterbacaan dan kemudahan pengkodean awal pengembangan.
sumber
List<MyCustomType>
danIEnumerator<MyCustomType> getListEnumerator() { }
baik-baik saja, sedangkan metodeIEnumerator getListEnumerator() { }
tidak akan.Jika a untuk setiap loop digunakan untuk koleksi atau array objek (yaitu array semua elemen selain tipe data primitif), GC (Pengumpul Sampah) dipanggil untuk membebaskan ruang variabel referensi di akhir a untuk setiap loop.
Sedangkan untuk loop digunakan untuk beralih pada elemen menggunakan indeks dan tipe data primitif tidak akan mempengaruhi kinerja dibandingkan dengan tipe data non-primitif.
sumber
temp
variabel di sini dapat dialokasikan pada stack, bahkan jika koleksi penuh referensi jenis (karena itu hanya referensi ke entri yang ada sudah di heap, tidak mengalokasikan memori heap baru untuk memegang sebuah contoh dari jenis referensi), sehingga kami tidak akan mengharapkan sampah terjadi di sini. Pencacah itu sendiri dapat dikotak dalam beberapa keadaan dan berakhir pada tumpukan, tetapi kasus-kasus tersebut berlaku untuk tipe data primitif juga.