Perangkat lunak ilmiah tidak jauh berbeda dari perangkat lunak lain, sejauh cara mengetahui apa yang perlu dicari.
Metode yang saya gunakan adalah jeda acak . Berikut adalah beberapa speedup yang telah saya temukan:
Jika sebagian besar waktu dihabiskan dalam fungsi seperti log
dan exp
, saya bisa melihat apa argumen untuk fungsi-fungsi itu, sebagai fungsi dari titik mereka dipanggil. Seringkali mereka dipanggil berulang kali dengan argumen yang sama. Jika demikian, memoisasi menghasilkan faktor percepatan besar-besaran.
Jika saya menggunakan fungsi BLAS atau LAPACK, saya mungkin menemukan bahwa sebagian besar waktu dihabiskan dalam rutinitas untuk menyalin array, mengalikan matriks, mengubah choleski, dll.
Rutin untuk menyalin array tidak ada untuk kecepatan, itu ada untuk kenyamanan. Anda mungkin menemukan ada cara yang kurang nyaman, tetapi lebih cepat, untuk melakukannya.
Rutinitas untuk menggandakan atau membalikkan matriks, atau mengambil transformasi choleski, cenderung memiliki argumen karakter yang menentukan opsi, seperti 'U' atau 'L' untuk segitiga atas atau bawah. Sekali lagi, itu ada untuk kenyamanan. Apa yang saya temukan adalah, karena matriks saya tidak terlalu besar, rutinitas menghabiskan lebih dari setengah waktu mereka memanggil subrutin untuk membandingkan karakter hanya untuk menguraikan pilihan. Menulis versi tujuan khusus dari rutinitas matematika yang paling mahal menghasilkan percepatan besar-besaran.
Jika saya dapat memperluas yang terakhir: matrix-multiply, DGEMM rutin memanggil LSAME untuk memecahkan kode argumen karakternya. Melihat persen waktu inklusif (satu-satunya statistik yang layak dilihat) yang dianggap profiler "baik" dapat menunjukkan DGEMM menggunakan beberapa persen dari total waktu, seperti 80%, dan LSAME menggunakan beberapa persen dari total waktu, seperti 50%. Melihat yang pertama, Anda akan tergoda untuk mengatakan "baik itu harus sangat dioptimalkan, jadi tidak banyak yang bisa saya lakukan tentang itu". Melihat yang terakhir, Anda akan tergoda untuk mengatakan "Hah? Tentang apa semua itu? Itu hanya sedikit rutinitas kecil. Profiler ini pasti salah!"
Itu tidak salah, itu hanya tidak memberi tahu Anda apa yang perlu Anda ketahui. Apa jeda acak menunjukkan kepada Anda adalah bahwa DGEMM ada di 80% dari sampel tumpukan, dan LSAME pada 50%. (Anda tidak perlu banyak sampel untuk mendeteksi itu. 10 biasanya banyak.) Terlebih lagi, pada banyak sampel tersebut, DGEMM sedang dalam proses memanggil LSAME dari beberapa baris kode yang berbeda.
Jadi sekarang Anda tahu mengapa kedua rutinitas menghabiskan banyak waktu inklusif. Anda juga tahu di mana dalam kode Anda mereka dipanggil dari untuk menghabiskan waktu ini. Itu sebabnya saya menggunakan jeda acak dan mengambil tampilan profiler yang kuning, tidak peduli seberapa bagus mereka. Mereka lebih tertarik mendapatkan pengukuran daripada memberi tahu Anda apa yang terjadi.
Sangat mudah untuk mengasumsikan rutin perpustakaan matematika telah dioptimalkan ke tingkat ke-n, tetapi pada kenyataannya mereka telah dioptimalkan untuk dapat digunakan untuk berbagai keperluan. Anda perlu melihat apa yang sebenarnya terjadi, bukan apa yang mudah diasumsikan.
TAMBAH: Jadi untuk menjawab dua pertanyaan terakhir Anda:
Apa hal terpenting yang harus dicoba terlebih dahulu?
Ambil 10-20 tumpukan sampel, dan jangan hanya meringkasnya, pahami apa yang diceritakan masing-masing. Lakukan ini dulu, terakhir, dan di antara keduanya. (Tidak ada "coba", Skywalker muda.)
Bagaimana saya tahu berapa banyak kinerja yang bisa saya dapatkan?
Sampel tumpukan akan memberi Anda perkiraan yang sangat kasar berapa fraksi waktu yang akan disimpan. (Ini mengikuti distribusi , di mana adalah jumlah sampel yang menampilkan apa yang akan Anda perbaiki, dan adalah jumlah total sampel. Itu tidak menghitung biaya kode yang Anda gunakan untuk menggantinya, yang diharapkan akan menjadi kecil.) Kemudian rasio speedup adalah yang bisa jadi besar. Perhatikan bagaimana ini berlaku secara matematis. Jika , dan , rata-rata dan mode adalah 0,5, untuk rasio percepatan 2. Berikut adalah distribusinya:
Jika Anda menghindari risiko, ya, ada kemungkinan kecil (0,03%) bahwaxβ(s+1,(n−s)+1)sn1/(1−x)n=10s=5x
x kurang dari 0,1, untuk speedup kurang dari 11%. Tetapi menyeimbangkan itu adalah probabilitas yang sama bahwa lebih besar dari 0,9, untuk rasio percepatan lebih dari 10! Jika Anda mendapatkan uang sebanding dengan kecepatan program, itu bukan peluang buruk.x
Seperti yang telah saya tunjukkan kepada Anda sebelumnya, Anda dapat mengulangi seluruh prosedur sampai Anda tidak bisa lagi, dan rasio percepatan yang diperparah bisa sangat besar.
TAMBAH: Dalam menanggapi kekhawatiran Pedro tentang positif palsu, izinkan saya mencoba membuat contoh di mana hal itu mungkin terjadi. Kami tidak pernah menindaklanjuti masalah yang potensial kecuali jika kami melihatnya dua kali atau lebih, jadi kami berharap kesalahan positif terjadi ketika kami melihat masalah pada waktu sesedikit mungkin, terutama ketika jumlah total sampel besar. Misalkan kita mengambil 20 sampel dan melihatnya dua kali. Yang memperkirakan biayanya adalah 10% dari total waktu eksekusi, mode distribusinya. (Rata-rata distribusi lebih tinggi - itu adalah .) Kurva yang lebih rendah pada grafik berikut adalah distribusinya:(s+1)/(n+2)=3/22=13.6%
Pertimbangkan jika kami mengambil sebanyak 40 sampel (lebih dari yang pernah saya miliki pada satu waktu) dan hanya melihat masalah pada dua dari mereka. Perkiraan biaya (mode) dari masalah itu adalah 5%, seperti yang ditunjukkan pada kurva yang lebih tinggi.
Apa itu "false positive"? Ini adalah bahwa jika Anda memperbaiki masalah Anda menyadari keuntungan yang lebih kecil dari yang diharapkan, bahwa Anda menyesal telah memperbaikinya. Kurva menunjukkan (jika masalahnya "kecil") itu, sementara gain bisa kurang dari fraksi sampel yang menunjukkan itu, rata-rata akan lebih besar.
Ada risiko yang jauh lebih serius - "negatif palsu". Saat itulah ada masalah, tetapi tidak ditemukan. (Menyumbang pada hal ini adalah "bias konfirmasi", di mana ketiadaan bukti cenderung diperlakukan sebagai bukti ketidakhadiran.)
Apa yang Anda dapatkan dengan profiler (yang baik) adalah Anda mendapatkan pengukuran yang jauh lebih tepat (kesempatan sehingga kurang positif palsu), dengan mengorbankan banyak informasi yang kurang tepat tentang apa masalah sebenarnya adalah (sehingga kurang kesempatan untuk menemukan itu dan mendapatkan keuntungan apapun ). Itu membatasi speedup keseluruhan yang bisa dicapai.
Saya akan mendorong pengguna profiler untuk melaporkan faktor percepatan yang sebenarnya mereka dapatkan dalam praktik.
Ada hal lain yang harus dibuat kembali. Pertanyaan Pedro tentang positif palsu.
Dia menyebutkan mungkin ada kesulitan ketika turun ke masalah kecil dalam kode yang sangat optimal. (Bagi saya, masalah kecil adalah yang menyumbang 5% atau kurang dari total waktu.)
Karena sepenuhnya mungkin untuk membangun program yang sepenuhnya optimal kecuali 5%, poin ini hanya dapat diatasi secara empiris, seperti dalam jawaban ini . Untuk menggeneralisasi dari pengalaman empiris, seperti ini:
Suatu program, seperti tertulis, biasanya mengandung beberapa peluang untuk optimisasi. (Kita dapat menyebutnya "masalah" tetapi mereka sering merupakan kode yang sangat baik, hanya mampu melakukan perbaikan yang cukup besar.) Diagram ini menggambarkan program buatan yang memakan waktu lama (100-an, katakanlah), dan berisi masalah A, B, C, ... itu, ketika ditemukan dan diperbaiki, hemat 30%, 21%, dll dari 100-an asli.
Perhatikan bahwa masalah F berharga 5% dari waktu semula, sehingga "kecil", dan sulit ditemukan tanpa 40 atau lebih sampel.
Namun, 10 sampel pertama dengan mudah menemukan masalah A. ** Ketika itu diperbaiki, program hanya membutuhkan 70-an, untuk percepatan 100/70 = 1,43x. Itu tidak hanya membuat program lebih cepat, ia memperbesar, dengan rasio itu, persentase diambil oleh masalah yang tersisa. Misalnya, masalah B awalnya mengambil 21 yang merupakan 21% dari total, tetapi setelah menghapus A, B mengambil 21 dari 70-an, atau 30%, sehingga lebih mudah untuk menemukan ketika seluruh proses diulang.
Setelah proses diulang lima kali, sekarang waktu eksekusi adalah 16,8 detik, dari yang masalah F adalah 30%, bukan 5%, sehingga 10 sampel merasa mudah.
Jadi itu intinya. Secara empiris, program mengandung serangkaian masalah yang memiliki distribusi ukuran, dan setiap masalah yang ditemukan dan diperbaiki membuatnya lebih mudah untuk menemukan yang tersisa. Untuk mencapai hal ini, tidak ada masalah yang dapat dilewati karena, jika memang ada, mereka duduk di sana mengambil waktu, membatasi total speedup, dan gagal memperbesar masalah yang tersisa.
Itu sebabnya sangat penting untuk menemukan masalah yang bersembunyi .
Jika masalah A sampai F ditemukan dan diperbaiki, kecepatannya adalah 100 / 11.8 = 8.5x. Jika salah satu dari mereka terlewat, misalnya D, maka percepatannya hanya 100 / (11,8 + 10.3) = 4.5x.
Itulah harga yang dibayarkan untuk negatif palsu.
Jadi, ketika profiler mengatakan "sepertinya tidak ada masalah yang signifikan di sini" (yaitu pengkode yang baik, ini adalah kode yang secara praktis optimal), mungkin itu benar, dan mungkin tidak. (A false negative .) Anda tidak tahu pasti apakah ada lebih banyak masalah untuk diperbaiki, untuk percepatan yang lebih tinggi, kecuali jika Anda mencoba metode profil lain dan menemukan bahwa ada. Dalam pengalaman saya, metode pembuatan profil tidak memerlukan sejumlah besar sampel, diringkas, tetapi sejumlah kecil sampel, di mana setiap sampel dipahami cukup menyeluruh untuk mengenali setiap peluang untuk optimasi.
** Diperlukan minimal 2 klik pada masalah untuk menemukannya, kecuali seseorang memiliki pengetahuan sebelumnya bahwa ada (dekat) loop tak terbatas. (Tanda centang merah mewakili 10 sampel acak); Jumlah rata-rata sampel yang diperlukan untuk mendapatkan 2 hit atau lebih, ketika masalahnya adalah 30%, adalah ( distribusi binomial negatif ). 10 sampel menemukannya dengan probabilitas 85%, 20 sampel - 99,2% ( distribusi binomial ). Untuk mendapatkan probabilitas untuk menemukan masalah, di R, mengevaluasi , misalnya: .2/0.3=6.671 - pbinom(1, numberOfSamples, sizeOfProblem)
1 - pbinom(1, 20, 0.3) = 0.9923627
TAMBAH: Fraksi waktu yang dihemat, , mengikuti distribusi Beta , di mana adalah jumlah sampel, dan adalah jumlah yang menampilkan masalah. Namun, rasio percepatan sama dengan (dengan asumsi semua disimpan), dan akan menarik untuk memahami distribusi . Ternyata mengikuti distribusi BetaPrime . Saya mensimulasikannya dengan 2 juta sampel, sampai pada perilaku ini:β ( s + 1 , ( n - s ) + 1 ) n s y 1 / ( 1 - x ) x y y - 1xβ(s+1,(n−s)+1)nsy1/(1−x)xyy−1
distribution of speedup
ratio y
s, n 5%-ile 95%-ile mean
2, 2 1.58 59.30 32.36
2, 3 1.33 10.25 4.00
2, 4 1.23 5.28 2.50
2, 5 1.18 3.69 2.00
2,10 1.09 1.89 1.37
2,20 1.04 1.37 1.17
2,40 1.02 1.17 1.08
3, 3 1.90 78.34 42.94
3, 4 1.52 13.10 5.00
3, 5 1.37 6.53 3.00
3,10 1.16 2.29 1.57
3,20 1.07 1.49 1.24
3,40 1.04 1.22 1.11
4, 4 2.22 98.02 52.36
4, 5 1.72 15.95 6.00
4,10 1.25 2.86 1.83
4,20 1.11 1.62 1.31
4,40 1.05 1.26 1.14
5, 5 2.54 117.27 64.29
5,10 1.37 3.69 2.20
5,20 1.15 1.78 1.40
5,40 1.07 1.31 1.17
Dua kolom pertama memberikan interval kepercayaan 90% untuk rasio percepatan. Rasio kecepatan rata-rata sama dengan kecuali untuk kasus di mana . Dalam hal itu tidak terdefinisi dan, memang, ketika saya meningkatkan jumlah nilai simulasi , rata-rata empiris meningkat.s = n y(n+1)/(n−s)s=ny
Ini adalah plot distribusi faktor percepatan, dan artinya, untuk 2 hit dari 5, 4, 3, dan 2 sampel. Misalnya, jika 3 sampel diambil, dan 2 di antaranya merupakan hit pada suatu masalah, dan masalah itu dapat dihilangkan, faktor percepatan rata-rata adalah 4x. Jika 2 hit terlihat hanya dalam 2 sampel, speedup rata-rata tidak terdefinisi - secara konseptual karena program dengan loop tak terbatas ada dengan probabilitas tidak nol!