Kami sedang mengerjakan basis kode C ++ berukuran sedang (10Mloc) yang melalui upaya optimalisasi kami menjadi sangat lambat .
Basis kode ini adalah satu set perpustakaan yang kami gabungkan untuk membuatnya berfungsi. Ketika kerangka umum bagaimana perpustakaan ini berkomunikasi dikembangkan ada beberapa penekanan pada kinerja dan kemudian, ketika lebih banyak bagian di mana ditambahkan, kerangka umum tidak banyak berubah. Optimalisasi dilakukan ketika dibutuhkan dan seiring dengan perkembangan perangkat keras kami. Ini membuat keputusan awal yang mahal terlihat hanya beberapa saat kemudian. Kami sekarang pada titik di mana optimasi lebih lanjut jauh lebih mahal karena mereka akan membutuhkan penulisan ulang sebagian besar basis kode. Kami menemukan diri kami mendekati minimum lokal yang tidak diinginkan karena kami tahu bahwa pada prinsipnya kode harus dapat berjalan lebih cepat.
Apakah ada metodologi yang berhasil yang membantu memutuskan apa yang akan mengambil alih evolusi basis kode menuju solusi yang bekerja secara global yang optimal yang tidak mudah dikacaukan oleh peluang optimisasi yang mudah?
EDIT
Untuk menjawab pertanyaan bagaimana kami saat ini profil:
Kami benar-benar hanya memiliki 2 skenario berbeda bagaimana kode ini dapat digunakan, keduanya paralel paralel. Pembuatan profil dilakukan baik dengan waktu jam dinding yang dirata-ratakan atas sampel besar input dan proses yang lebih rinci (biaya instruksi, misprediksi cabang, dan masalah caching). Ini bekerja dengan baik karena kami berjalan secara eksklusif pada mesin kami yang sangat homogen (sekelompok beberapa ribu mesin yang identik). Karena kami biasanya membuat semua mesin kami sibuk sebagian besar waktu berjalan lebih cepat berarti kami dapat melihat hal-hal baru tambahan. Masalahnya tentu saja bahwa ketika variasi input baru muncul, mereka mungkin mendapatkan hukuman terlambat karena kami menghapus inefisiensi mikro yang paling jelas untuk kasus penggunaan lainnya, sehingga mungkin mempersempit jumlah skenario "berjalan secara optimal".
sumber
sloc
. Saya menyebutnya "ukuran sedang" karena saya tidak tahu apa yang dianggap "besar" di sini.Jawaban:
Saya tidak tahu tentang pendekatan tujuan umum untuk masalah ini, tetapi dua pendekatan yang agak terkait bekerja dengan baik bagi saya di masa lalu: karena kurangnya istilah yang lebih baik, saya menyebut mereka pengelompokan agregasi dan optimasi horizontal .
Pendekatan Bunching adalah upaya untuk mengganti sejumlah besar operasi pendek, cepat dengan operasi tunggal, berjalan lebih lambat, yang sangat terspesialisasi yang pada akhirnya menghasilkan hasil yang sama.
Contoh: Setelah membuat profil satu operasi yang sangat lambat dari editor aturan visual kami, kami tidak menemukan "buah yang tergantung rendah": tidak ada satu operasi pun yang mengambil lebih dari 2% dari waktu eksekusi, namun operasi secara keseluruhan terasa lamban. Namun, kami menemukan bahwa editor mengirim sejumlah besar permintaan kecil ke server. Meskipun editor dengan cepat memproses balasan individu, jumlah interaksi permintaan / tanggapan memiliki efek multiplikatif, sehingga keseluruhan waktu operasi berlangsung beberapa detik. Setelah dengan hati-hati membuat katalog interaksi editor selama operasi yang berjalan lama itu, kami menambahkan perintah baru ke antarmuka server. Perintah tambahan lebih khusus, karena menerima data yang diperlukan untuk melakukan subset operasi singkat, mengeksplorasi dependensi data untuk mengetahui set data terakhir yang akan dikembalikan, dan memberikan respons yang berisi informasi yang diperlukan untuk menyelesaikan semua operasi kecil individu dalam satu perjalanan ke server. Ini tidak mengurangi waktu pemrosesan dalam kode kami, tetapi mengurangi jumlah latensi yang sangat signifikan karena menghapus beberapa perjalanan pulang-pergi klien-server yang mahal.
Optimalisasi horizontal adalah teknik terkait ketika Anda menghilangkan "kelambatan" yang didistribusikan secara tipis di antara beberapa komponen sistem Anda menggunakan fitur tertentu dari lingkungan eksekusi Anda.
Contoh: Setelah membuat profil operasi yang berjalan lama kami menemukan bahwa kami membuat banyak panggilan melintasi batas domain aplikasi (ini sangat spesifik untuk .NET). Kami tidak dapat menghilangkan salah satu panggilan, dan kami tidak dapat mengelompokkannya: mereka datang pada waktu yang berbeda dari bagian sistem kami yang sangat berbeda, dan hal-hal yang mereka minta bergantung pada hasil yang dikembalikan dari permintaan sebelumnya. Setiap panggilan diperlukan serialisasi dan deserialisasi dari sejumlah kecil data. Sekali lagi, masing-masing panggilan berdurasi pendek, tetapi jumlahnya sangat besar. Kami akhirnya merancang skema yang menghindari serialisasi hampir seluruhnya, menggantinya dengan melewatkan pointer melintasi batas domain aplikasi. Ini adalah kemenangan besar, karena banyak permintaan dari kelas yang sepenuhnya tidak berhubungan langsung menjadi lebih cepat sebagai hasil dari penerapan satusolusi horisontal .
sumber
Ketika Anda memulai penulisan ulang ini, Anda harus melakukan beberapa hal secara berbeda.
Pertama. Dan yang paling penting. Berhenti "mengoptimalkan". "Optimasi" sama sekali tidak masalah. Seperti yang Anda lihat, hanya menulis ulang grosir yang penting.
Karena itu.
Kedua. Memahami implikasi dari setiap struktur data dan pilihan algoritma.
Ketiga. Buat pilihan aktual dari struktur data dan algoritme soal "keterlambatan pengikatan". Desain antarmuka yang dapat memiliki salah satu dari beberapa implementasi yang digunakan di belakang antarmuka.
Apa yang Anda lakukan sekarang (menulis ulang) harus jauh, jauh lebih tidak menyakitkan jika Anda memiliki seperangkat antarmuka yang memungkinkan Anda untuk membuat perubahan besar pada struktur data atau algoritma.
sumber
++
dan+=1
akan menjadi tidak relevan dan hampir tidak terukur. Ini hal yang Anda bertahan lama .Trik praktis yang bagus adalah dengan menggunakan unit test unit Anda sebagai suite test kinerja .
Pendekatan berikut bekerja dengan baik di basis kode saya:
Jika Anda terus melakukan semua ini, maka seiring waktu kinerja rata-rata basis kode Anda akan meningkat secara organik.
Mungkin juga untuk melacak waktu pengujian historis dan menggambar grafik kinerja dan melihat regresi dari waktu ke waktu dalam kinerja rata-rata. Saya tidak pernah repot dengan ini terutama karena agak sulit untuk memastikan Anda membandingkan suka dengan seperti saat Anda mengubah dan menambahkan tes baru, tetapi itu bisa menjadi latihan yang menarik jika kinerja cukup penting bagi Anda.
sumber
Jawaban oleh @dasblinkenlight menunjukkan masalah yang sangat umum, terutama dengan basis kode besar (dalam pengalaman saya). Mungkin ada masalah kinerja yang serius, tetapi tidak terlokalisasi . Jika Anda menjalankan profiler, tidak ada rutinitas yang membutuhkan waktu cukup lama, untuk menarik perhatian. (Asumsikan Anda melihat persentase waktu inklusif yang mencakup anak sapi. Jangan repot-repot dengan "waktu sendiri".)
Bahkan, dalam kasus itu, masalah sebenarnya tidak ditemukan oleh profiling, tetapi oleh keberuntungan.
Saya punya studi kasus, yang ada slide show PDF singkat , menggambarkan masalah ini secara rinci dan cara menanganinya. Poin dasarnya adalah, karena kodenya jauh lebih lambat dari yang seharusnya, itu berarti (menurut definisi) kelebihan waktu dihabiskan untuk melakukan sesuatu yang bisa dihapus.
Jika Anda melihat beberapa sampel waktu acak dari status program, Anda hanya akan melihatnya melakukan aktivitas yang dapat dilepas, karena persentase waktu yang diperlukan. Sangat mungkin aktivitas yang dapat dilepas tidak terbatas pada satu fungsi, atau bahkan banyak fungsi. Itu tidak melokalisasi seperti itu.
Itu bukan "hot spot".
Ini adalah deskripsi yang Anda buat dari apa yang Anda lihat, yang kebetulan benar dalam persentase besar waktu. Itu membuatnya mudah ditemukan, tetapi apakah mudah untuk memperbaikinya tergantung pada seberapa banyak penulisan ulang yang diperlukan.
(Ada kritik yang sering dibuat dari pendekatan ini, bahwa jumlah sampel terlalu kecil untuk validitas statistik. Itu dijawab pada slide 13 dari PDF. Secara singkat - ya, ada ketidakpastian yang tinggi dalam "pengukuran" penghematan potensial, tetapi 1) nilai yang diharapkan dari penghematan potensial itu pada dasarnya tidak terpengaruh, dan 2) ketika potensi penghematan $ x $ diterjemahkan ke dalam rasio kenaikan sebesar $ 1 / (1-x) $, itu sangat condong ke faktor-faktor (menguntungkan) yang tinggi.)
sumber