Apakah analisis algoritmik dengan penghitungan gagal sudah usang?

43

Dalam kursus analisis numerik saya, saya belajar untuk menganalisis efisiensi algoritma dengan menghitung jumlah operasi floating-point (jepit) yang mereka butuhkan, relatif terhadap ukuran masalah. Misalnya, dalam teks Trefethen & Bau pada Numerical Linear Algebra, bahkan ada gambar 3D yang tampak dari jumlah kegagalan.

Sekarang modis untuk mengatakan bahwa "jepit gratis" karena latensi memori untuk mengambil apa pun yang tidak ada dalam cache jauh lebih besar daripada biaya kegagalan. Tapi kami masih mengajar siswa untuk menghitung jepit, setidaknya dalam kursus analisis numerik. Haruskah kita mengajar mereka untuk menghitung akses memori? Apakah kita perlu menulis buku pelajaran baru? Atau apakah akses memori terlalu spesifik mesin untuk menghabiskan waktu? Apa tren jangka panjang yang akan terjadi dalam hal apakah kegagalan atau akses memori adalah hambatannya?

Catatan: beberapa jawaban di bawah ini sepertinya menjawab pertanyaan yang berbeda seperti "Haruskah saya menulis ulang implementasi saya secara obsesif untuk menghemat beberapa kegagalan atau meningkatkan kinerja cache?" Tetapi apa yang saya tanyakan lebih pada baris " Apakah lebih berguna untuk memperkirakan kompleksitas algoritmik dalam hal operasi aritmatika atau akses memori ?"

David Ketcheson
sumber
1
> "Apakah lebih berguna untuk memperkirakan kompleksitas algoritmik dalam hal operasi aritmatika atau akses memori?" . Dari sudut pandang praktis, sistem embedded masih dibatasi oleh kecepatan FPU daripada bandwidth memori. Dengan demikian, bahkan jika penghitungan jepit dianggap usang oleh standar HPC, itu masih berguna untuk masyarakat lain.
Damien

Jawaban:

31

Saya pikir (urutan pertama) yang benar untuk dilakukan adalah melihat rasio jepit ke byte yang diperlukan dalam algoritma, yang saya sebut . Biarkan menjadi flop rate maksimum prosesor, dan bandwidth maksimum. Jika , maka algoritma akan dibatasi bandwidth. Jika , algoritma ini terbatas.F m a x B m a x F m a xβFmaxBmaxBmaxβ>FmaxFmaxβ>BmaxBmaxβ>Fmax

Saya pikir menghitung akses memori adalah wajib, tetapi kita juga harus memikirkan:

  • Berapa banyak memori lokal diperlukan

  • Berapa banyak kemungkinan konkurensi yang kita miliki

Kemudian Anda dapat mulai menganalisis algoritma untuk perangkat keras modern.

Matt Knepley
sumber
3
Saya setuju dengan Matt, tetapi saya ingin menunjukkan bahwa sekarang cukup umum ditemukan didefinisikan dalam literatur sebagai "intensitas aritmatika" dan "intensitas numerik". Saya pikir model Roofline oleh Williams, Waterman, dan Patterson mungkin merupakan awal yang baik untuk memikirkan masalah ini. Saya pikir ini harus diperluas ke memori rasio / kegagalan akses dalam waktu. β
Aron Ahmadia
2
David melakukan lebih banyak 8 tahun sebelumnya.
Matt Knepley
3
Oke, jadi ada model yang lebih baik, lebih kompleks (seperti biasa). Tetapi model ini memberikan jawaban yang bergantung pada mesin. Apa yang harus kita ajarkan kepada siswa untuk digunakan sebagai analisis pertama?
David Ketcheson
3
Intinya adalah bahwa mesin telah direduksi menjadi satu angka, rasio puncak jepit ke bandwidth puncak, seperti halnya algoritma. Ini sesederhana yang didapat. Tanpa model komputasi, setiap perkiraan kompleksitas tidak berguna dan ini adalah realistis yang paling sederhana.
Matt Knepley
1
Saya pikir Anda salah paham masalah. Kami sudah memiliki transportasi optik yang dapat membawa beban besar. Masalahnya adalah mendapatkan chip. Anda hanya memiliki begitu banyak kabel dan laju clock tertinggi. Transport optik hanya akan meringankan masalah ini pada chip optik.
Matt Knepley
22

Saya tidak mengerti mengapa seseorang harus menjadi "pemenang"; ini bukan permainan zero-sum, di mana gagal dan akses memori harus menenggelamkan yang lain. Anda dapat mengajar mereka berdua, dan saya pikir mereka berdua memiliki kegunaannya. Bagaimanapun, sulit untuk mengatakan bahwa algoritma dengan akses memori tentu akan lebih cepat daripada algoritma dengan akses . Itu semua tergantung pada biaya relatif dari bagian-bagian yang berbeda (prefactor sial yang selalu kita abaikan dalam analisis ini!).O ( N ) O ( N log N ) O ( N 2 )O(N4)O(N)O(NlogN)O(N2)

Dari perspektif yang lebih luas, saya berpikir bahwa analisis kinerja algoritmik harus "menyeluruh". Jika kita mengajar orang untuk menjadi pengembang dan pengguna HPC yang sebenarnya, maka mereka perlu memahami berapa biaya pemrograman di dunia nyata. Model analisis abstrak yang kami miliki tidak memperhitungkan waktu programmer. Kita harus berpikir dalam hal "total waktu untuk solusi," daripada hanya menghitung kegagalan dan efisiensi algoritmik. Masuk akal untuk menghabiskan tiga atau empat hari programmer untuk menulis ulang rutin yang akan menghemat satu detik waktu komputer per pekerjaan kecuali Anda berencana menjalankan beberapa juta perhitungan. Demikian pula, investasi beberapa hari untuk menghemat satu atau dua jam waktu perhitungan dengan cepat terbayar. Algoritma novel itu mungkin luar biasa,

aeismail
sumber
7
Sebuah algoritma yang melakukan akses data? :)O ( N 2 )O(NlogN)O(N2)
Andreas Klöckner
2
Kenapa tidak? Jika hanya mengacu pada ops floating point, mungkin ada tambahan sejumlah besar ops integer yang menyebabkan akses data yang :)O ( N 2 )O(NlogN)O(N2)
kini
9

Seperti yang telah ditunjukkan oleh orang lain, jawabannya tentu saja tergantung pada apakah hambatannya adalah CPU atau bandwidth memori. Untuk banyak algoritme yang bekerja pada beberapa dataset berukuran acak, bottleneck biasanya adalah bandwidth memori karena dataset tidak cocok dengan cache CPU.

Selain itu, Knuth menyebutkan bahwa analisis akses memori lebih mungkin untuk bertahan dalam ujian waktu, mungkin karena itu relatif sederhana (bahkan ketika memperhitungkan cache-friendly) dibandingkan dengan kompleksitas pipeline CPU modern dan prediksi cabang.

Knuth menggunakan istilah gigamem dalam Volume 4A dari TAOCP, ketika menganalisis BDD. Saya tidak yakin apakah dia menggunakannya dalam volume sebelumnya. Dia membuat komentar di atas tentang bertahan dalam ujian waktu dalam Kuliah Pohon Natal tahunannya pada tahun 2010.

Menariknya, You're Doing It Wrong menunjukkan bahwa bahkan menganalisis kinerja berdasarkan operasi memori tidak selalu mudah karena ada unsur-unsur seperti tekanan VM yang ikut berperan jika data tidak masuk ke dalam RAM fisik sekaligus.

Jason Davies
sumber
8

Bagaimana Anda menentukan biaya suatu algoritma tergantung pada "level" komputasi ilmiah mana Anda bekerja, dan kelas masalah (sempit atau luas) mana yang Anda pertimbangkan.

Jika Anda berpikir tentang optimasi cache, ini jelas lebih relevan untuk, misalnya, implementasi paket aljabar linier numerik seperti BLAS dan perpustakaan serupa. Jadi ini milik optimasi tingkat rendah, dan tidak apa-apa jika Anda memiliki algoritme tetap untuk masalah tertentu dan dengan kendala yang cukup pada input. Misalnya, optimasi Cache mungkin relevan untuk memiliki implementasi cepat dari iterasi gradien konjugasi jika matriks dijanjikan cukup jarang.

Di sisi lain, semakin luas kelas masalah, semakin sedikit Anda dapat memprediksi pada komputasi yang sebenarnya (seperti, katakanlah, Anda tidak tahu seberapa jarang matriks input implementasi CG Anda akan benar-benar). Semakin luas kelas mesin yang harus dijalankan oleh program Anda, semakin sedikit Anda dapat memprediksi arsitektur Cache.

Selain itu, pada tingkat komputasi ilmiah yang lebih tinggi, mungkin lebih relevan untuk mengubah struktur masalah. Misalnya, jika Anda menghabiskan waktu mencari prekondisi yang bagus untuk sistem persamaan linear, optimasi semacam ini biasanya mengalahkan optimasi tingkat rendah, karena jumlah iterasi berkurang secara drastis.

Dalam kesimpulan, optimisasi cache hanya berguna jika tidak ada yang tersisa untuk dioptimalkan oleh paralelisme dan pengurangan jumlah FLOP yang asimptotik.

Saya pikir itu bijaksana untuk mengadaptasi sikap ilmu komputer teoritis: Pada akhirnya, meningkatkan kompleksitas asimptotik dari suatu algoritma memiliki lebih banyak pengembalian daripada optimasi mikro dari beberapa baris kode yang ada. Oleh karena itu, penghitungan FLOP masih lebih disukai.

shuhalo
sumber
msgstr "optimisasi cache hanya berguna jika tidak ada yang tersisa untuk dioptimalkan oleh paralelisme dan pengurangan jumlah FLOP asimptotik". Saya tidak setuju. Jika Anda ingin menghitung ekspresi besar dari sejumlah besar angka, lebih baik melakukan satu langkah pada satu waktu dengan semua angka daripada semua langkah untuk setiap angka. Keduanya memiliki jumlah FLOPS yang sama, tetapi satu lebih baik dalam akses memori. Bonus jika Anda memilih ukuran gugus yang sesuai dengan cache (atau kompiler melakukannya untuk Anda). Inilah yang dilakukan numexpr dalam Python: github.com/pydata/numexpr
Davidmh
6

Saya selalu menolak untuk berpikir tentang menghitung jepit, akses memori, atau apa pun yang Anda miliki. Itu konsep dari tahun 1960-an ketika apa yang Anda lakukan cukup banyak diberikan dan hanya bagaimana Anda melakukannya terserah optimasi algoritmik. Pikirkan memecahkan masalah elemen hingga pada mesh xyz yang seragam menggunakan eliminasi Gaussian dari iterasi Jacobi.

Sekarang, Anda dapat mengoptimalkan ini ke neraka dan menghemat beberapa jepit, mendapatkan 10% dari waktu berjalan. Atau Anda dapat berpikir tentang menerapkan metode multigrid dan blok preconditioner yang optimal, mendapatkan faktor 10 dalam waktu berjalan. Inilah yang harus kita latih untuk dilakukan oleh siswa kita - pikirkan tentang apa, algoritma luar yang kompleks dapat membuat Anda lebih dari mencoba menemukan algoritma dalam yang lebih baik. Atasan Anda (Keyes) memiliki slide-slide ini tentang kemajuan dalam perhitungan MHD yang membuat poin ini sangat jelas.

Wolfgang Bangerth
sumber
Sebenarnya saya bertanya tentang jenis pemikiran tingkat tinggi yang Anda sarankan, bukan optimasi tingkat rendah. Metrik apa yang harus Anda gunakan untuk menentukan apakah multigrid dan prekondisi Anda akan lebih cepat daripada alternatifnya?
David Ketcheson
Saya tidak akan tahu bagaimana cara menghitung - dengan tangan - FLOPS atau penghitungan instruksi lainnya untuk algoritme kompleks yang dijalankan lebih dari puluhan atau ribuan baris kode. Pikirkan, misalnya, betapa rumitnya fase analisis dan konstruksi algoritma AMG. Ada begitu banyak bagian dari algoritma ini, dan semua itu tergantung pada data aktual sehingga Anda tidak dapat memprediksi jumlah operasi.
Wolfgang Bangerth
1
Saya pikir saya awalnya salah mengerti apa yang Anda maksud, tapi saya masih tidak setuju dengan maksud Anda. "Algoritma luar" dapat (dan saya berpendapat, seharusnya) masih dirancang dengan kompleksitas asimtotik dalam pikiran. Tentunya Anda tidak akan mengklaim bahwa penurunan dari algoritma kuadrat ke algoritma linear dekat akan paling baik menyebabkan pengurangan 10% dalam runtime; namun, bagaimana lagi untuk menghitung kompleksitas asimptotik selain melalui kegagalan dan / atau operasi memori?
Jack Poulson
7
Saya pikir pendekatan "angkat tangan" untuk algoritma ini adalah omong kosong. Anda perlu menyederhanakan analisis dengan hanya melihat biaya orde pertama, dan dengan menyederhanakan model sehingga dapat ditelusuri, tetapi untuk mengatakan Anda tidak dapat menganalisis sesuatu seperti MG atau Cholesky karena terlalu rumit adalah salah.
Matt Knepley
1
Baiklah, tetapi apa artinya menganalisis MG atau Cholesky ketika setiap FLOP yang Anda hitung tersembunyi di balik beberapa lapisan latensi yang disebabkan oleh prosesor yang di-hiphread, cache, RAM lambat, prosesor multiscalar, dan vektorisasi otomatis? Maksud saya adalah bahwa dalam faktor 5-10, Anda tidak dapat memprediksi run-time dari algoritma Anda lagi tanpa waktu itu. Itu benar-benar berbeda di tahun 50-an dan 60-an ketika orang-orang mulai menghitung FLOP ini.
Wolfgang Bangerth
1

Ya usang. Analisis algoritmik dengan jepit, atau metode lain apa pun, hanya berguna seperti model abstrak mesin saat mempertimbangkan ukuran masalah yang dihadapi. Kinerja aktual tergantung baik pada implementasi dan perangkat keras, dan penerapan model abstrak apa pun untuk yang terakhir terhadap kenyataan semakin menurun dari waktu ke waktu. Sebagai contoh, ketika Anda lebih lanjut memparalelkan penerapan algoritma yang kompleks, seperti dinamika molekuler, aspek yang berbeda menjadi batasan kecepatan pada perangkat keras yang berbeda, dan analisis algoritmik tidak ada hubungannya dengan pengamatan. Di satu sisi, satu-satunya hal yang penting adalah mengukur kinerja implementasi algoritma pada tipe perangkat keras yang bersangkutan.

Apakah abstraksi semacam itu bermanfaat sebagai alat pembelajaran? Ya, seperti banyak model yang digunakan untuk mengajar, mereka berguna selama mereka ditempatkan di samping pemahaman tentang keterbatasan model. Mekanika klasik baik-baik saja selama Anda menghargai bahwa itu tidak akan bekerja pada skala jarak kecil atau besar ...

mabraham
sumber
-1

Tidak benar-benar menjawab pertanyaan Anda, tetapi lebih menambahkan variabel lain untuk dipertimbangkan: sesuatu yang harus diperhitungkan adalah fitur dari bahasa pemrograman. Misalnya, Python sortmenggunakan algoritma Timsort , yang dirancang (di antara properti bagus lainnya) untuk meminimalkan jumlah perbandingan, yang bisa berpotensi lambat untuk objek Python. Di sisi lain, membandingkan dua float di C ++ sangat cepat, tetapi menukar mereka lebih mahal, sehingga mereka menggunakan algoritma lain .

Contoh lain adalah alokasi memori dinamis (sepele dalam daftar Python, cepat dalam waktu runtime dan waktu pengembang, adil .append()), vs FORTRAN atau C, di mana, meskipun mungkin dan lebih cepat bila diterapkan dengan benar, dibutuhkan waktu dan otak pemrograman yang jauh lebih besar. Lihat Python lebih cepat dari FORTRAN.

Davidmh
sumber
Ini benar, tetapi, seperti yang Anda katakan, tidak menjawab pertanyaan. Ini tentang topik yang berbeda.
David Ketcheson
Nah, dalam analisis yang tepat itu adalah sesuatu yang harus dipertimbangkan ketika memutuskan algoritma mana yang akan diimplementasikan.
Davidmh