Ada dua area untuk mengoptimalkan kecepatan:
- Di mana sebagian besar waktu dihabiskan
- Kode itulah yang paling disebut
Mana tempat terbaik untuk mulai mengoptimalkan?
Seringkali kode yang disebut paling sering memiliki waktu eksekusi yang rendah. Apakah Anda mengoptimalkan area yang lebih lambat, kurang dipanggil atau menghabiskan waktu mengoptimalkan area yang lebih cepat dan banyak digunakan?
optimization
profiling
Michael K.
sumber
sumber
Jawaban:
Anda harus mengabaikan efisiensi kecil 95% dari waktu. Pertama, buat itu berfungsi dengan benar , kemudian analisis ...
Desain Anda
Pilihan Anda dari algoritma tingkat tinggi dapat memiliki dampak besar pada keseluruhan kinerja perangkat lunak Anda, ke titik di mana satu pilihan yang tampaknya sepele dapat berarti perbedaan antara menunggu 20 menit untuk memulai program dan memiliki UI yang cepat dan responsif.
Misalnya, dalam game 3D: jika Anda mulai dengan daftar objek sederhana untuk grafik adegan Anda, Anda akan melihat kinerja yang sangat buruk untuk sejumlah objek yang relatif kecil; tetapi jika Anda malah menerapkan hierarki volume (seperti octree atau BVH) dan menyisihkan bagian-bagian pohon saat menggambar, Anda akan melihat peningkatan kinerja besar-besaran.
Ketika desain Anda tampaknya benar, maka Anda dapat beralih ke ...
Logika tingkat rendah.
Algoritma tingkat rendah juga dapat memiliki dampak yang signifikan. Ketika melakukan pemrosesan gambar, misalnya, jika Anda membaca gambar dalam urutan yang salah, Anda akan mengalami penurunan yang sangat besar saat Anda mengalami cache L2 yang terus-menerus hilang; menata ulang operasi Anda bisa berarti peningkatan kinerja sepuluh kali lipat.
Pada titik ini, buat profil dan temukan tempat di mana sebagian besar waktu program dihabiskan, dan temukan cara untuk menghilangkannya.
sumber
Pertama, jalankan profiler untuk mencari tahu di mana kode Anda menghabiskan waktunya.
Kemudian, lihat tempat-tempat itu untuk melihat mana yang terlihat mudah dioptimalkan.
Cari perbaikan termudah yang akan mendapatkan keuntungan terbesar terlebih dahulu (pilih buah yang mudah digantung). Jangan terlalu khawatir tentang betapa pentingnya hal itu, tepatnya. Jika mudah, perbaiki. Itu akan bertambah. 25 perbaikan mudah mungkin lebih cepat dari 1 perbaikan besar, dan efek kumulatifnya mungkin lebih besar. Jika sulit, buat catatan atau ajukan laporan bug sehingga Anda dapat memprioritaskannya nanti. Jangan terlalu khawatir tentang "besar" atau "kecil" pada saat ini - lakukan saja, sampai Anda mendapatkan fungsi yang menggunakan sedikit waktu. Setelah Anda melakukan ini, Anda harus memiliki gagasan yang lebih baik tentang masalah mana yang telah Anda temukan yang mungkin mendapatkan kemenangan terbesar dengan investasi waktu paling sedikit.
Jangan lupa untuk menindaklanjuti profil setelah perbaikan Anda sebagai semacam uji regresi, untuk memverifikasi bahwa perubahan kinerja Anda memiliki efek yang Anda harapkan. Juga, jangan lupa untuk menjalankan paket regresi Anda, untuk memastikan tidak ada fungsi yang rusak. Terkadang kinerja buruk menunjukkan penyelesaian, dan mencoba memperbaiki kinerja akan merusak fungsionalitas.
Fungsi kecil yang tidak dapat dioptimalkan tetapi menggunakan banyak waktu mungkin masih menjadi petunjuk tentang di mana harus mengoptimalkan. Mengapa fungsi itu disebut begitu banyak? Apakah ada fungsi memanggil fungsi kecil yang tidak perlu terlalu banyak menggunakannya? Apakah pekerjaan digandakan, atau pekerjaan yang tidak perlu dilakukan? Lihat tumpukan untuk berapa kali dipanggil sampai Anda yakin itu harus sering dipanggil, dan lihat apakah Anda menemukan fungsi yang lebih besar dengan algoritma yang tidak efisien.
Diedit untuk menambahkan: Karena Anda memiliki fungsionalitas khusus yang membutuhkan waktu lama, coba lakukan langkah-langkah di atas hanya dengan fungsi spesifik yang dijalankan 10 kali atau lebih.
sumber
Sulit dikatakan. Ini sangat tergantung pada apa yang dilakukan kode. Jalankan tes kinerja, dapatkan profil kinerja, dan lihat serta lihat berapa banyak waktu aktual yang dihabiskan di berbagai bidang. Generalisasi Anda adalah ... generalisasi dan bervariasi dari proyek ke proyek.
Misalnya, kode yang dipanggil paling mungkin hanya masuk ke file atau konsol. Tidak ada gunanya mengoptimalkan bahwa jika sudah satu atau dua baris kode yang tidak dapat dibuat lebih sederhana, dan mungkin ada upaya untuk mengoptimalkan sesuatu seperti ini mungkin tidak sebanding dengan biaya sebenarnya coding itu. Kode yang paling tidak disebut bisa berupa permintaan berukuran monster yang digunakan dalam beberapa fungsi rumit yang mengerikan. Fungsi ini mungkin hanya dipanggil 100 kali selama seluruh eksekusi dijalankan (vs. 10.000 untuk pernyataan logging sederhana), tetapi jika dibutuhkan 20 detik untuk setiap waktu panggilan berjalan, mungkin di situlah optimasi harus dimulai? Atau bisa juga sebaliknya, dengan kueri besar yang paling sering dipanggil, dan pernyataan logging hanya memanggil satu untuk setiap 100 kueri ...
Saya biasanya tidak khawatir tentang hal semacam ini (sampai saya perlu melakukan tuning kinerja) kecuali saya punya ide sebelumnya apa yang akan terjadi.
sumber
Yah "kita" biasanya tidak mengoptimalkan sampai ada kebutuhan yang jelas untuk optimasi ketika ada sesuatu yang sangat lambat.
Dan ketika kebutuhan ini memanifestasikan dirinya, biasanya disertai dengan petunjuk yang baik tentang apa yang sebenarnya disebut untuk optimasi.
Jadi jawabannya biasa: "Tergantung."
sumber
Anda harus menggunakan profiler pada sejumlah proses biasa dan melihat total waktu yang dihabiskan di setiap bagian kode, tidak peduli seberapa sering Anda sampai di sana. Mengoptimalkan bagian-bagian ini harus selalu memberikan peningkatan kecepatan.
Bergantung pada seberapa rendah level bahasa implementasi Anda, Anda juga harus mengetahui bagian apa yang menyebabkan sebagian besar cache gagal. Mengkonsolidasikan kode panggilan akan membantu di sini.
sumber
Masalahnya adalah frasa "di mana waktu yang paling banyak dihabiskan" adalah ambigu.
Jika itu berarti "di mana penghitung program paling sering ditemukan" maka saya telah melihat program di mana waktu terbanyak dihabiskan dalam fungsi string, membandingkan memori, mengalokasikan fungsi perpustakaan matematika. Dengan kata lain, fungsi yang tidak boleh disentuh oleh programmer sehari-hari.
Jika itu berarti "di mana dalam kode programmer adalah pernyataan yang dieksekusi yang menghabiskan sebagian besar waktu" itu adalah konsep yang lebih berguna.
Masalah dengan konsep "kode yang disebut paling" adalah, jumlah waktu yang dibutuhkan adalah produk dari seberapa sering dipanggil dan berapa banyak waktu yang dibutuhkan per panggilan (termasuk callees dan I / O). Karena jumlah waktu yang dibutuhkan dapat bervariasi pada beberapa urutan besarnya, berapa kali disebut tidak memberi tahu Anda berapa banyak masalah itu. Fungsi A dapat disebut 10 kali dan mengambil 0,1 detik, sedangkan fungsi B dapat disebut 1000 kali dan mengambil mikrodetik.
Satu hal yang akan memberi tahu Anda di mana mencarinya adalah ini: Setiap kali satu baris kode menyebabkan waktu untuk dihabiskan, ia berada di tumpukan . Jadi, misalnya, jika garis kode adalah hot spot, atau jika itu adalah panggilan ke fungsi perpustakaan, atau jika itu adalah panggilan ke-20 dalam pohon panggilan 30-tingkat, jika itu bertanggung jawab untuk 20% dari waktu , maka itu di tumpukan 20% dari waktu. Sampel waktu-acak tumpukan akan masing-masing memiliki peluang 20% untuk menampilkannya. Terlebih lagi, jika sampel dapat diambil selama I / O, mereka akan menunjukkan kepada Anda apa yang menyebabkan I / O, yang bisa sama borosnya dengan siklus CPU yang terbuang.
Dan ini benar-benar independen dari berapa kali dipanggil.
sumber
Optimalkan di mana sebagian besar waktu dihabiskan kecuali ada alasan khusus untuk tidak (yaitu sebagian besar waktu dihabiskan melakukan pemrosesan asinkron yang manusia tidak benar-benar peduli apakah itu selesai dalam 5 menit atau 10 menit). Kode yang disebut paling, tentu saja, akan cenderung untuk mendapatkan porsi yang relatif besar dari total waktu yang telah berlalu hanya karena bahkan waktu eksekusi yang singkat bertambah ketika Anda melakukannya ribuan kali.
sumber
Anda harus mengerjakan kode yang paling banyak menghabiskan waktu. Meningkatkan kode yang hanya menyumbang beberapa persen dari waktu berjalan hanya dapat memberi Anda sedikit peningkatan.
Sudahkah Anda melakukan pengukuran sehingga Anda tahu kode mana yang paling banyak memakan waktu?
sumber
Saya biasa melakukan benchmarking dan pemasaran untuk vendor superkomputer, jadi mengalahkan kompetisi dengan cepat bukanlah hal yang paling penting, itu adalah SATU-SATUNYA hal. Hasil semacam itu mengharuskan Anda menggunakan kombinasi algorthm yang baik, dan struktur data yang memungkinkan sebagian besar CPU intensif menjalankan puncak secara efisien. Itu berarti Anda harus memiliki ide yang cukup bagus tentang operasi apa yang paling intensif secara komputasi, dan jenis struktur data apa yang memungkinkan mereka berjalan paling cepat. Kemudian itu adalah masalah membangun aplikasi di sekitar kernel / struktur data yang dioptimalkan.
Dalam arti yang lebih umum, tipikal setelah penyetelan fakta. Profil Anda, lihat hot spot, dan hot spot yang menurut Anda bisa Anda percepat adalah yang Anda kerjakan. Tetapi jarang pendekatan ini memberi Anda sesuatu yang dekat dengan implementasi tercepat yang mungkin.
Lebih umum lagi, (kesalahan algoritmik tidak bertahan), untuk mesin modern Anda harus berpikir bahwa kinerja ditentukan oleh tiga hal: akses data, akses data, dan akses data! Pelajari tentang hierarki memori (register, cache, TLB, halaman, dll.) Dan rancang struktur data Anda untuk memanfaatkannya sebaik mungkin. Secara umum ini berarti bahwa Anda ingin dapat menjalankan loop dalam footprint memori kompak. Jika Anda hanya menulis (atau diberikan) aplikasi dan kemudian mencoba untuk mengoptimalkannya, Anda biasanya dibebani dengan struktur data yang membuat penggunaan hirarki memori menjadi buruk, dan mengubah struktur data biasanya melibatkan latihan refactoring utama, jadi Anda harus sering macet.
sumber
Jika Anda ingin pengembalian atas upaya pengoptimalan, Anda harus melihat kode yang paling lama. Target umum saya adalah sesuatu yang membutuhkan setidaknya 80% dari waktu. Saya biasanya menargetkan kenaikan kinerja 10 kali. Ini terkadang membutuhkan perubahan besar dalam bagaimana kode itu dirancang. Perubahan semacam ini memberi Anda sesuatu yang berjalan kira-kira empat kali lebih cepat.
Keuntungan kinerja terbaik saya sepanjang waktu adalah mengurangi waktu berjalan dari 3 hari menjadi 9 menit. Kode yang saya optimalkan berubah dari 3 hari menjadi 3 menit. Aplikasi yang diganti aplikasi mengurangi ini menjadi 9 detik, tetapi itu membutuhkan perubahan dalam bahasa, dan penulisan ulang lengkap.
Mengoptimalkan aplikasi yang sudah cepat bisa menjadi tugas bodoh. Saya masih akan menargetkan area mengambil paling banyak waktu. Jika Anda dapat mengambil sesuatu menggunakan 10% dari waktu untuk kembali secara instan, Anda masih memerlukan 90% dari waktu. Anda dengan cepat menekan aturan pengembalian yang semakin berkurang.
Tergantung pada apa yang Anda optimalkan untuk aturan masih berlaku. Cari pengguna sumber daya utama dan optimalkan mereka. Jika sumber daya yang Anda optimalkan adalah bottleneck sistem, Anda mungkin menemukan bahwa yang Anda lakukan hanyalah mengubah bottleneck ke sumber daya lain.
sumber
Biasanya itu akan menjadi fungsi yang lebih kecil dalam banyak kasus, bukan fungsi yang disebut paling sering satu miliar kali dalam satu lingkaran.
Ketika Anda melakukan profiling berbasis sampel (dengan alat atau dengan tangan), sering hotspot terbesar akan berada di panggilan kecil berdaun yang melakukan hal-hal sederhana, seperti fungsi untuk membandingkan dua bilangan bulat.
Fungsi itu sering tidak akan mendapat manfaat dari optimasi yang banyak, jika ada. Paling tidak titik-titik granular itu jarang menjadi prioritas utama. Fungsi memanggil fungsi daun yang mungkin menjadi pembuat masalah, atau fungsi yang memanggil fungsi memanggil fungsi, seperti algoritma pengurutan yang kurang optimal. Dengan alat yang baik Anda dapat menelusuri dari callee ke penelepon, dan juga melihat siapa yang menghabiskan waktu paling banyak memanggil callee.
Sering kali merupakan kesalahan untuk terobsesi pada callees dan tidak melihat penelepon ke bawah grafik panggilan dalam sesi profil kecuali Anda melakukan hal-hal yang sangat tidak efisien di tingkat mikro. Kalau tidak, Anda mungkin terlalu berkeringat pada hal-hal kecil dan kehilangan gambaran besar. Hanya memiliki profiler di tangan tidak melindungi Anda dari terobsesi pada hal-hal sepele. Itu hanya langkah pertama ke arah yang benar.
Anda juga harus memastikan bahwa Anda membuat profil operasi yang selaras dengan hal-hal yang sebenarnya ingin dilakukan oleh pengguna, jika tidak, akan benar-benar disiplin dan ilmiah dalam pengukuran dan tolok ukur Anda tidak berharga karena tidak selaras dengan apa yang dilakukan pelanggan dengan produk. Saya memiliki seorang kolega suatu kali yang menyetel keluar dari algoritma pembagian untuk membagi kubus menjadi satu miliar segi dan dia sangat bangga dengan itu .... kecuali pengguna tidak membagi kubus 6-poligon sederhana menjadi satu miliar segi. Semuanya melambat menjadi merangkak ketika mencoba berjalan pada model mobil produksi dengan lebih dari 100.000 poligon untuk dibagi, di mana titik itu bahkan tidak bisa melakukan 2 atau 3 tingkat subdivisi tanpa memperlambat merangkak. Sederhananya dia menulis kode yang super dioptimalkan untuk ukuran input kecil yang tidak realistis yang tidak
Anda harus mengoptimalkan kasus penggunaan nyata yang selaras dengan minat pengguna Anda atau yang lebih buruk daripada tidak berharga, karena semua optimasi yang cenderung setidaknya agak mengurangi pemeliharaan kode memiliki sedikit manfaat pengguna dan hanya semua negatif untuk basis kode.
sumber