Secara umum seberapa sering dan kapan saya harus mengoptimalkan kode saya?


13

Pada langkah normal 'optimasi pemrograman bisnis sering dibiarkan sampai benar-benar diperlukan. Berarti Anda tidak boleh optmize sampai benar-benar dibutuhkan.

Ingat apa yang dikatakan Donald Knuth "Kita harus melupakan efisiensi kecil, katakanlah sekitar 97% dari waktu: optimasi prematur adalah akar dari semua kejahatan"

Kapan waktu untuk mengoptimalkan untuk memastikan saya tidak membuang-buang usaha. Haruskah saya melakukannya pada level metode? Tingkat kelas? Tingkat modul?

Juga apa yang harus saya ukur optimasi? Kutu? Frame Rate? Total Waktu?

Jawaban:


18

Di tempat saya bekerja, kami selalu menggunakan beberapa tingkat profil; jika Anda melihat masalah, Anda hanya perlu sedikit ke bawah daftar sampai Anda mengetahui apa yang terjadi:

  • "Profiler manusia", alias hanya bermain game ; apakah itu terasa lambat atau "hambatan" sesekali? Melihat animasi tersentak-sentak? (Sebagai pengembang, perhatikan bahwa Anda akan lebih peka terhadap beberapa jenis masalah kinerja dan tidak memperhatikan yang lain. Rencanakan pengujian tambahan yang sesuai.)
  • Nyalakan tampilan FPS , yang merupakan jendela geser rata-rata 5 detik FPS. Sangat sedikit overhead untuk dihitung dan ditampilkan.
  • Nyalakan bilah profil , yang hanya serangkaian paha depan (warna ROYGBIV) yang mewakili berbagai bagian bingkai (mis. Vblank, preframe, pembaruan, tabrakan, render, postframe) menggunakan timer "stopwatch" sederhana di sekitar setiap bagian kode . Untuk menekankan apa yang kami inginkan, kami menetapkan satu bar lebar layar untuk mewakili bingkai target 60Hz, sehingga sangat mudah untuk melihat apakah Anda mis. 50% di bawah anggaran (hanya setengah bar) atau 50% lebih dari ( batang membungkus dan menjadi satu setengah batang). Ini juga cukup mudah untuk mengetahui apa yang umumnya memakan sebagian besar frame: red = render, yellow = update, dll ...
  • Bangun instrumen khusus yang memasukkan "stopwatch" seperti kode di sekitar setiap fungsi. (Perhatikan bahwa Anda dapat mengambil hit kinerja, dcache, dan icache besar-besaran ketika melakukan ini, jadi itu pasti mengganggu. Tetapi jika Anda tidak memiliki profiler pengambilan sampel yang tepat atau dukungan yang layak pada CPU, ini adalah pilihan yang dapat diterima. Anda juga bisa menjadi pintar tentang merekam minimal data pada fungsi, masuk / keluar dan membangun kembali jejak panggilan nanti.) Ketika kami membangun milik kami, kami menirukan banyak format output gprof .
  • Yang terbaik dari semuanya, jalankan sampling profiler ; VTune dan CodeAnalyst tersedia untuk x86 dan x64, Anda memiliki berbagai simulasi atau lingkungan emulasi yang mungkin memberi Anda data di sini.

(Ada kisah menyenangkan dari GDC tahun lalu tentang seorang programmer grafis yang mengambil empat gambar dirinya - bahagia, acuh tak acuh, jengkel, dan marah - dan menampilkan gambar yang sesuai di sudut internal builds berdasarkan framerate. The pembuat konten dengan cepat belajar untuk tidak mengaktifkan shader yang rumit untuk semua objek dan lingkungan mereka: mereka akan membuat programmer marah. Lihatlah kekuatan umpan balik.)

Perhatikan bahwa Anda juga dapat melakukan hal-hal menyenangkan seperti membuat grafik "bilah profil" terus menerus, sehingga Anda dapat melihat pola lonjakan ("kami kehilangan bingkai setiap 7 frame") atau sejenisnya.

Namun, untuk menjawab pertanyaan Anda secara langsung: dalam pengalaman saya, sementara itu menggoda (dan seringkali bermanfaat - saya biasanya belajar sesuatu) untuk menulis ulang fungsi / modul tunggal untuk mengoptimalkan jumlah instruksi atau kinerja icache atau dcache, dan kami benar-benar perlu melakukan ini kadang-kadang ketika kita punya masalah kinerja yang sangat menjengkelkan, sebagian besar masalah kinerja yang kita tangani secara teratur datang ke desain . Sebagai contoh:

  • Haruskah kita cache dalam RAM atau memuat ulang dari frame animasi "serangan" negara untuk pemain? Bagaimana dengan setiap musuh? Kami tidak memiliki RAM untuk melakukan semuanya, tetapi beban disk mahal! Anda dapat melihat halangan jika 5 atau 6 musuh yang berbeda muncul sekaligus! (Oke, bagaimana dengan pemijahan yang mengejutkan?)
  • Apakah kita melakukan satu jenis operasi di semua partikel, atau semua operasi di satu partikel? (Ini adalah tradeoff icache / dcache, dan jawabannya tidak selalu jelas.) Bagaimana kalau memisahkan semua partikel dan menyimpan posisi bersama-sama ("struct of arrays" yang terkenal)) vs. menjaga semua data partikel di satu tempat (" array struct ").

Anda mendengarnya sampai menjadi menjengkelkan di setiap program ilmu komputer tingkat universitas, tetapi: itu benar-benar tentang struktur data dan algoritma. Menghabiskan waktu pada algoritma dan desain aliran data akan membuat Anda mendapatkan lebih banyak uang secara umum. (Pastikan Anda telah membaca Kesulitan Pemrograman Berorientasi Objek slide rekan Layanan Pengembang Sony untuk beberapa wawasan di sini.) Ini tidak "terasa" seperti optimisasi; sebagian besar waktu dihabiskan dengan papan tulis atau alat UML atau membuat banyak prototipe, daripada membuat kode saat ini berjalan lebih cepat. Tetapi secara umum jauh lebih bermanfaat.

Dan heuristik lain yang bermanfaat: jika Anda dekat dengan "inti" mesin Anda, mungkin perlu upaya dan percobaan ekstra untuk mengoptimalkan (misalnya, vektorkan matriks yang berlipat ganda itu!). Semakin jauh dari inti, semakin sedikit Anda harus khawatir tentang itu kecuali salah satu alat profil Anda memberi tahu Anda sebaliknya.


6
  1. Gunakan struktur data dan algoritma yang tepat di depan.
  2. Jangan mengoptimalkan mikro sampai Anda profil dan tahu persis di mana hot spot Anda.
  3. Jangan khawatir tentang menjadi pintar. Kompiler sudah melakukan semua trik kecil yang Anda pikirkan ("Oh! Saya perlu mengalikan empat! Saya akan bergeser ke kiri dua!")
  4. Perhatikan kesalahan cache.

1
Mengandalkan kompiler hanya pintar untuk titik tertentu. Ya, itu akan melakukan beberapa optimasi lubang intip yang tidak akan Anda pikirkan (dan tidak bisa dilakukan tanpa perakitan), tetapi tidak mengerti tentang apa yang seharusnya dilakukan algoritma Anda sehingga tidak dapat melakukan optimasi cerdas. Juga, Anda akan terkejut betapa banyak siklus yang bisa Anda menangkan dengan menerapkan kode kritis dalam perakitan atau intrinsik .... jika Anda tahu apa yang Anda lakukan. Kompiler tidak sepintar apa adanya, mereka tidak tahu hal-hal yang Anda lakukan kecuali Anda memberi tahu mereka secara eksplisit di mana-mana (seperti menggunakan 'membatasi' secara agama).
Kaj

1
Dan sekali lagi saya harus berkomentar bahwa jika Anda hanya mencari titik panas Anda akan kehilangan banyak siklus karena Anda tidak akan menemukan siklus tetesan di papan (misalnya smartpointers .... referensi di mana saja, tidak pernah muncul sebagai hotspot karena secara efektif seluruh program Anda adalah hotspot).
Kaj

1
Saya setuju dengan kedua poin Anda, tetapi saya akan menggumpalkan sebagian besar di bawah "gunakan struktur data yang tepat dan algoritma." Jika Anda melewati pointer pintar yang dihitung ulang di mana-mana dan sedang mengalami siklus penghitungan, Anda pasti memilih struktur data yang salah.
munificent

5

Namun ingat juga "pesimisasi dini". Meskipun tidak perlu melakukan hardcore pada setiap baris kode, ada justifikasi untuk menyadari bahwa Anda benar-benar bekerja pada sebuah game, yang memiliki implikasi kinerja waktu-nyata.
Meskipun semua orang memberi tahu Anda untuk mengukur dan mengoptimalkan hot-spot, teknik itu tidak akan menunjukkan kepada Anda kinerja yang hilang di tempat tersembunyi. Misalnya, jika setiap operasi '+' dalam kode Anda akan memakan waktu dua kali lebih lama dari yang seharusnya, itu tidak akan muncul sebagai hot-spot dan dengan demikian Anda tidak akan pernah mengoptimalkannya atau bahkan menyadarinya, namun karena ini digunakan di luar tempat itu mungkin dikenakan biaya banyak kinerja. Anda akan terkejut betapa banyak dari siklus itu mengalir keluar tanpa pernah terdeteksi. Jadi sadarilah apa yang Anda lakukan.
Terlepas dari itu, saya cenderung membuat profil secara teratur untuk mendapatkan ide tentang apa yang ada di sana, dan berapa banyak waktu yang tersisa per frame. Bagi saya waktu per frame adalah yang paling logis karena ia memberitahu saya secara langsung di mana saya berada dengan tujuan framerate. Juga coba cari tahu di mana puncaknya dan apa yang menyebabkannya - Saya lebih suka framerate stabil daripada framerate tinggi dengan paku.


Ini kelihatannya salah bagi saya. Tentu, '+' saya mungkin memakan waktu dua kali lebih lama setiap kali dipanggil, tetapi ini benar-benar hanya penting dalam lingkaran yang ketat. Di dalam loop ketat, mengubah satu '+' dapat melakukan urutan besarnya lebih dari mengubah '+' di luar loop. Mengapa berpikir tentang sepersepuluh mikrodetik, ketika satu milidetik dapat disimpan?
Wilduck

1
Maka Anda tidak mengerti ide di balik tetesan menetes. '+' (hanya sebagai contoh) disebut ratusan ribu kali per frame, bukan hanya di loop ketat. Jika itu kehilangan beberapa siklus setiap kali Anda kehilangan banyak siklus di seluruh papan, tetapi itu tidak akan pernah muncul sebagai hotspot karena panggilan didistribusikan secara merata di seluruh basis kode / jalur eksekusi Anda. Jadi Anda tidak berbicara tentang sepersepuluh mikrodetik, tetapi memang ribuan kali sepersepuluh mikrodetik itu, bertambah hingga beberapa milidetik. Setelah pergi untuk buah yang menggantung rendah (loop ketat) saya memperoleh milidetik dengan cara ini lebih dari sekali.
Kaj

Ini seperti keran yang menetes. Mengapa khawatir tentang menyelamatkan setetes itu? - "Jika keran Anda menetes pada kecepatan satu tetes per detik, Anda dapat berharap untuk membuang 2.700 galon per tahun".
Kaj

Oh, saya kira itu tidak jelas maksud saya ketika operator + kelebihan beban, jadi itu akan mempengaruhi setiap '+' dalam kode - Anda memang tidak ingin mengoptimalkan setiap '+' dalam kode. Contoh buruk kurasa .... Aku bermaksud sebagai contoh untuk 'fungsionalitas inti yang dipanggil di semua tempat di mana implementasi mungkin lebih lambat daripada yang diasumsikan, terutama ketika disembunyikan oleh operator yang berlebihan atau konstruksi C ++ yang membingungkan'.
Kaj

3

Setelah gim siap dirilis (baik final atau beta), atau terasa lambat, itu mungkin waktu terbaik untuk membuat profil aplikasi Anda. Tentu saja, Anda selalu dapat menjalankan profiler di titik mana pun; tapi ya, optimasi prematur adalah akar dari semua kejahatan. Optimasi tanpa dasar juga; Anda memerlukan data aktual untuk menunjukkan bahwa beberapa kode lambat, sebelum Anda harus mencoba "mengoptimalkannya". Profiler melakukan itu untuk Anda.

Jika Anda tidak tahu tentang profiler, pelajari itu! Berikut adalah posting blog yang bagus yang menunjukkan kegunaan profiler.

Sebagian besar pengoptimalan kode permainan diturunkan untuk mengurangi siklus CPU yang Anda butuhkan untuk setiap frame. Salah satu cara untuk melakukan ini adalah hanya mengoptimalkan setiap rutin saat Anda menulisnya, dan pastikan itu secepat mungkin. Namun, ada pepatah umum bahwa 90% dari siklus CPU dihabiskan dalam 10% dari kode. Ini berarti bahwa mengarahkan semua pekerjaan optimasi Anda ke rutinitas bottleneck ini akan memiliki 10x efek mengoptimalkan semuanya secara seragam. Jadi bagaimana Anda mengidentifikasi rutinitas ini? Pembuatan profil memudahkan.

Jika tidak, jika gim kecil Anda berjalan pada 200 FPS meskipun memiliki algoritma yang tidak efisien, apakah Anda benar-benar memiliki alasan untuk mengoptimalkan? Anda harus memiliki gagasan yang baik tentang spesifikasi mesin target Anda, dan pastikan gim tersebut berjalan dengan baik pada mesin itu, tetapi apa pun di luar itu (bisa dibilang) terbuang waktu yang bisa lebih baik dihabiskan untuk coding atau memoles gim.


Sementara buah yang menggantung rendah memang cenderung berada di 10% dari kode, dan akan mudah ditangkap dengan membuat profil pada akhirnya, murni bekerja dengan membuat profil untuk ini akan membuat Anda kehilangan rutinitas yang disebut banyak tetapi hanya memiliki sedikit sedikit kode buruk masing-masing - mereka tidak akan muncul di profil Anda tetapi mereka mengalami banyak siklus per panggilan. Itu benar-benar bertambah.
Kaj

@ Kaj, Profiler yang baik menjumlahkan semua ratusan eksekusi individual dari algoritma buruk dan menunjukkan kepada Anda totalnya. Selanjutnya Anda akan berkata, "Tetapi bagaimana jika Anda memiliki 10 metode yang buruk dan semuanya memanggil frekuensi 1/10?" Jika Anda menghabiskan seluruh waktu Anda untuk 10 metode itu, Anda akan kehilangan semua buah yang tergantung rendah di mana Anda akan mendapatkan pukulan yang jauh lebih besar untuk uang Anda.
John McDonald

2

Saya merasa bermanfaat untuk membangun profil. Bahkan jika Anda tidak secara optimal mengoptimalkannya, ada baiknya untuk mengetahui apa yang membatasi kinerja Anda pada waktu tertentu. Banyak game memiliki semacam HUD overlay yang menampilkan grafik grafik sederhana (biasanya hanya bar berwarna) yang menunjukkan berapa lama berbagai bagian dari loop game mengambil setiap frame.

Ini akan menjadi ide yang buruk untuk meninggalkan analisis kinerja dan optimasi terlambat pada tahap akhir. Jika Anda sudah membuat game dan Anda 200% di atas anggaran CPU Anda dan Anda tidak dapat menemukannya melalui optimasi, Anda kacau.

Anda perlu tahu apa anggaran untuk grafik, fisika, dll, saat Anda menulis. Anda tidak dapat melakukan itu jika Anda tidak tahu apa yang akan terjadi dengan kinerja Anda, dan Anda tidak dapat menebaknya tanpa mengetahui apa kinerja Anda, dan berapa banyak kelonggaran di sana.

Jadi, buatlah beberapa statistik kinerja sejak hari pertama.

Mengenai kapan menangani masalah - lagi, mungkin lebih baik jangan sampai terlambat, jangan sampai Anda harus memperbaiki setengah mesin Anda. Di sisi lain, jangan terlalu sibuk dengan hal-hal mengoptimalkan untuk memeras setiap siklus jika Anda berpikir Anda mungkin mengubah algoritma sepenuhnya besok, atau jika Anda belum memasukkan data game nyata melalui itu.

Angkat buah rendah tergantung saat Anda pergi, menangani hal-hal besar secara berkala, dan Anda harus baik-baik saja.


Untuk menambah ingame profiler (yang sangat saya setujui), memperluas ingame profiler Anda untuk menampilkan beberapa bar (untuk beberapa frame) membantu Anda menghubungkan perilaku permainan dengan paku dan mungkin membantu Anda menemukan kemacetan yang tidak akan muncul dalam tangkapan rata-rata Anda dengan profiler.
Kaj

2

Jika melihat kutipan Knuth dalam konteksnya ia melanjutkan dengan menjelaskan, bahwa kita harus mengoptimalkan tetapi dengan alat, seperti profiler.

Anda harus terus-menerus membuat profil dan memori profil aplikasi Anda setelah arsitektur yang sangat dasar diletakkan.

Profiling tidak hanya akan membantu Anda meningkatkan kecepatan, itu akan membantu Anda menemukan bug. Jika program Anda tiba-tiba mengubah kecepatan secara drastis, ini biasanya karena bug. Jika Anda tidak membuat profil, mungkin tidak diketahui.

Trik untuk mengoptimalkan adalah melakukannya dengan desain. Jangan tunggu sampai menit terakhir. Pastikan desain program Anda memberi Anda kinerja yang Anda butuhkan tanpa benar-benar pada trik lingkaran dalam yang jahat.


1

Untuk proyek saya, saya biasanya menerapkan beberapa optimasi SANGAT dibutuhkan di mesin dasar saya. Sebagai contoh, saya selalu ingin mengimplementasikan implementasi SIMD padat yang baik menggunakan SSE2 dan 3DNow! Ini memastikan matematika titik apung saya sesuai dengan yang saya inginkan. Praktik baik lainnya adalah membuat kebiasaan dari optimisasi saat Anda membuat kode alih-alih kembali. Sebagian besar waktu praktik-praktik kecil ini sama memakan waktu dengan apa yang Anda coding. Sebelum membuat kode fitur, pastikan Anda meneliti cara paling efisien untuk melakukannya.

Intinya, menurut saya, KERAS untuk membuat kode Anda lebih efisien setelah sudah payah.


0

Saya akan mengatakan bahwa cara termudah adalah menggunakan akal sehat Anda - jika sesuatu terlihat berjalan lambat, maka lihatlah. Lihat apakah itu hambatan.
Gunakan profiler untuk melihat fungsi kecepatan yang diambil dan seberapa sering mereka dipanggil.
Sama sekali tidak ada gunanya mengoptimalkan atau menghabiskan waktu mencoba mengoptimalkan sesuatu yang tidak membutuhkannya.


0

Jika kode Anda berjalan lambat, kemudian jalankan profiler dan lihat apa yang menyebabkannya berjalan lebih lambat. Atau Anda bisa proaktif dan sudah membuat profiler berjalan sebelum Anda mulai melihat masalah kinerja.

Anda akan ingin mengoptimalkan ketika framerate Anda turun ke titik di mana permainan mulai menderita. Pelakunya yang paling mungkin adalah CPU Anda terlalu banyak digunakan (100%).


Saya akan mengatakan GPU sama besar kemungkinannya dengan CPU. Memang, tergantung pada seberapa erat hal-hal yang digabungkan, sangat mungkin untuk menjadi sangat terikat CPU di setengah frame, dan berat GPU mengikat setengah lainnya. Profil bodoh bahkan dapat menunjukkan pemanfaatan yang kurang dari 100% pada keduanya. Pastikan profil Anda cukup berbutir halus untuk menunjukkan hal itu (tetapi tidak berbutir halus sehingga mengganggu!)
JasonD

0

Anda harus mengoptimalkan kode Anda ... sesering yang Anda butuhkan.

Apa yang telah saya lakukan di masa lalu hanya terus menjalankan permainan dengan profil diaktifkan (setidaknya konter framerate di layar setiap saat). Jika permainan semakin lambat (di bawah target framerate Anda di mesin min spec, misalnya), nyalakan profiler dan lihat apakah ada hot spot muncul.

Terkadang bukan kodenya. Banyak masalah yang saya alami di masa lalu berorientasi pada GPU (memang, ini ada di iPhone). Isi masalah, terlalu banyak panggilan, tidak cukup geometri, shader tidak efisien ...

Selain algoritma yang tidak efisien untuk masalah-masalah yang sulit (yaitu merintis jalan, fisika), saya sudah sangat jarang mengalami masalah di mana kode itu sendiri adalah biang keladinya. Dan masalah-masalah sulit itu haruslah hal-hal yang Anda habiskan banyak upaya Anda untuk mendapatkan algoritma yang benar dan tidak khawatir tentang hal-hal kecil.


0

Bagi saya adalah yang terbaik ikuti model data yang dipersiapkan dengan baik. Dan optimalisasi-sebelum langkah utama ke depan. Maksud saya sebelum mulai menerapkan sesuatu yang baru. Alasan lain untuk optimalisasi adalah ketika saya kehilangan kontrol pada sumber daya, App membutuhkan banyak beban CPU / beban GPU atau memori dan saya tidak tahu mengapa :) atau terlalu banyak.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.