Apakah pemrograman modular memengaruhi waktu komputasi?

19

Semua orang mengatakan bahwa saya harus membuat kode saya modular, tetapi bukankah kurang efisien jika saya menggunakan lebih banyak pemanggilan metode daripada metode yang lebih sedikit, tetapi lebih besar? Apa perbedaan dalam Java, C, atau C ++ dalam hal ini?

Saya mendapatkan bahwa lebih mudah untuk mengedit, membaca, dan memahami, terutama dalam sebuah grup. Jadi, apakah kerugian waktu komputasi tidak signifikan dibandingkan dengan manfaat kerapian kode?

fatsokol
sumber
2
Pertanyaannya adalah berapa lama waktu yang dibutuhkan untuk menghemat waktu pemrosesan dan menghabiskan waktu yang dihabiskan untuk perawatan yang lebih sulit. Jawabannya tergantung sepenuhnya pada aplikasi Anda.
Blrfl
2
Banyak pertanyaan bagus menghasilkan beberapa tingkat pendapat berdasarkan pengalaman ahli, tetapi jawaban untuk pertanyaan ini cenderung hampir seluruhnya didasarkan pada pendapat, bukan fakta, referensi, atau keahlian khusus.
nyamuk
10
Penting juga untuk menunjukkan bahwa hukuman komputasi untuk panggilan fungsi atau metode sangat kecil sehingga bahkan dalam program yang sangat besar dengan banyak fungsi dan panggilan metode, membuat panggilan-panggilan itu bahkan tidak masuk peringkat pada bagan.
greyfade
1
@ Greyfade: Itu berlaku untuk lompatan langsung tetapi lompatan tidak langsung tambahan diperkirakan akan menelan biaya misalnya ~ 3% dari total waktu berjalan program (hanya angka dari program yang baru-baru ini saya periksa - mungkin belum representatif). Bergantung pada area Anda, Anda mungkin atau mungkin tidak menganggapnya signifikan tetapi itu mendaftar pada grafik (dan tentu saja setidaknya sebagian ortogonal ke modularitas).
Maciej Piechotka
4
Optimalisasi prematur adalah akar dari semua kejahatan. Kode linier sedikit lebih cepat daripada kode modular. Kode modular jauh lebih cepat daripada kode spageti. Jika Anda membidik kode linier tanpa proyek yang sangat menyeluruh (SANGAT) dari semuanya, Anda akan berakhir dengan kode spaghetti, saya jamin itu.
SF.

Jawaban:

46

Ya, itu tidak relevan.

Komputer adalah mesin eksekusi yang hampir tak kenal lelah, hampir sempurna yang bekerja pada kecepatan yang sama sekali tidak dapat dibandingkan dengan otak. Meskipun ada jumlah waktu yang dapat diukur yang ditambahkan oleh fungsi panggil ke waktu eksekusi suatu program, ini tidak ada artinya dibandingkan dengan waktu tambahan yang dibutuhkan oleh otak orang berikutnya yang terlibat dengan kode ketika mereka harus mengurai rutinitas yang tidak dapat dibaca. untuk bahkan mulai memahami cara bekerja dengannya. Anda dapat mencoba perhitungan untuk lelucon - asumsikan bahwa kode Anda harus dipertahankan hanya sekali , dan itu hanya menambah setengah jam dari waktu yang dibutuhkan seseorang untuk menerima kode tersebut. Ambil kecepatan jam prosesor Anda dan hitung: berapa kali kode harus dijalankan bahkan untuk mimpi mengimbangi itu?

Singkatnya, kasihan pada CPU sepenuhnya, benar-benar sesat 99,99% dari waktu. Untuk sisa kasus yang jarang terjadi, gunakan profiler. Apakah tidak berasumsi bahwa Anda dapat melihat kasus-kasus - Anda tidak bisa.

Kilian Foth
sumber
2
Sementara saya setuju bahwa sebagian besar waktu itu adalah optimasi prematur, saya pikir argumen Anda tentang waktu itu buruk. Waktu itu relatif dalam situasi yang berbeda dan Anda tidak bisa begitu saja menghitung seperti yang Anda lakukan.
Honza Brabec
28
+1 hanya untuk ungkapan "kasihan pada CPU", karena itu dengan baik menekankan kesalahan kepala dalam optimasi prematur.
Michael Borgwardt
4
Sangat terkait: Seberapa cepat komputer dalam hal sehari-hari? Pergi keluar dari jalan Anda untuk menghindari beberapa panggilan fungsi untuk "kecepatan" seperti pergi keluar dari jalan Anda untuk menyelamatkan seseorang satu menit total selama seluruh hidup mereka . Singkatnya: itu bahkan tidak sepadan dengan waktu yang diperlukan untuk mempertimbangkan.
BlueRaja - Danny Pflughoeft
7
+1 untuk begitu fasih menjual apa yang banyak hilang, tetapi Anda lupa menambahkan bobot paling penting pada keseimbangan yang membuat mengasihani CPU bahkan lebih buruk: Mengabaikan kehilangan uang dan waktu yang dihabiskan untuk pemeliharaan satu kali itu; jika butuh 1 detik, masih ada wastafel yang jauh lebih berbahaya di sana. Risiko bug . Semakin membingungkan dan sulit bagi pengelola nantinya untuk mengubah kode Anda, semakin besar risiko dia menerapkan bug di dalamnya, yang dapat menyebabkan biaya tak terelakkan yang sangat besar dari risiko kepada pengguna dan bug apa pun yang menyebabkan pemeliharaan lanjutan (yang dapat menyebabkan pemeliharaan lanjutan) bug ...)
Jimmy Hoffa
Seorang guru lama saya biasa berkata, "Jika Anda berpikir apakah Anda terlalu dini mengoptimalkan atau tidak, berhenti di sini. Jika ini adalah pilihan yang tepat, Anda pasti tahu."
Ven
22

Tergantung.

Dalam dunia yang sangat lambat yaitu pemrograman Web, di mana segala sesuatu terjadi pada kecepatan manusia, pemrograman metode-berat, di mana biaya pemanggilan metode sebanding dengan atau melebihi biaya pemrosesan yang dilakukan oleh metode, mungkin tidak masalah .

Dalam dunia pemrograman sistem tertanam dan penangan interupsi untuk interupsi tingkat tinggi, itu tentu saja penting. Dalam lingkungan itu, model biasa "akses memori murah" dan "prosesornya sangat cepat" mogok. Saya telah melihat apa yang terjadi ketika seorang programmer berorientasi objek mainframe menulis penangan interrupt tingkat tinggi pertamanya. Itu tidak cantik.

Beberapa tahun yang lalu, saya melakukan pewarnaan gumpalan konektivitas 8 arah non-rekursif pada citra FLIR waktu-nyata, yang pada waktu itu merupakan prosesor yang baik. Upaya pertama menggunakan panggilan subrutin, dan panggilan subrutin memakan prosesor hidup-hidup. (4 panggilan PER PIXEL x 64K piksel per frame x 30 frame per detik = Anda mengetahuinya). Upaya kedua mengubah subrutin menjadi makro C, tanpa kehilangan keterbacaan, dan semuanya mawar.

Anda harus melihat KERAS pada apa yang Anda lakukan dan pada lingkungan di mana Anda akan melakukannya.

John R. Strohm
sumber
Ingatlah bahwa sebagian besar masalah pemrograman modern paling baik ditangani dengan banyak kode berorientasi objek, metode-senang. Anda akan tahu kapan Anda berada di lingkungan sistem tertanam.
Kevin - Reinstate Monica
4
+1, tetapi dengan peringatan: biasanya kompiler pengoptimalan sering melakukan pekerjaan yang lebih baik yaitu inlining, loop unrolling, dan melakukan optimasi skalar dan vektor daripada seseorang. Programmer masih perlu tahu bahasa, kompiler dan mesin dengan sangat baik untuk mengambil keuntungan dari ini, jadi itu bukan sihir.
detly
2
Itu benar tetapi Anda mengoptimalkan kode Anda setelah Anda menulis logika dan memprofilkannya untuk menemukan bagian yang bermasalah dalam algoritma Anda. Anda tidak mulai membuat kode untuk kinerja tetapi untuk keterbacaan. Dan makro akan membantu Anda agar kode Anda dapat dibaca.
Uwe Plonus
2
Bahkan dalam pemrograman sistem tertanam, mulailah dengan menulis kode yang benar dan baru mulai mengoptimalkan setelah itu jika perlu dan sebagaimana dipandu oleh profil . Kalau tidak, terlalu mudah untuk menempatkan banyak pekerjaan di yang tidak memiliki efek nyata pada ukuran kinerja / kode dan yang hanya membuat sumber sulit dimengerti.
Donal Fellows
1
@detly: Ya dan tidak. Kompiler modern secara umum dapat melakukan pekerjaan yang lebih baik DALAM BATAS YANG DITERAPKAN OLEH BAHASA. Pemrogram manusia tahu apakah batasan yang diberlakukan oleh bahasa dapat diterapkan dalam kasus tertentu. Sebagai contoh, kompiler Cn dan C ++ diperlukan oleh standar bahasa untuk memungkinkan programmer melakukan hal yang sangat patologis, dan menghasilkan kode yang tetap bekerja. Si programmer mungkin tahu dia tidak gila atau bodoh atau cukup berani untuk melakukan hal-hal itu, dan membuat optimasi yang kalau tidak akan aman, karena dia tahu mereka aman dalam hal ini.
John R. Strohm
11

Pertama-tama: Program dalam bahasa yang lebih tinggi adalah untuk dibaca oleh manusia bukan oleh mesin.

Jadi, tuliskan programnya sehingga Anda memahaminya. Jangan berpikir tentang kinerja (jika Anda memiliki masalah kinerja serius maka profil aplikasi Anda dan tingkatkan kinerja di mana diperlukan).

Bahkan jika memang benar bahwa memanggil suatu metode atau fungsi membutuhkan beberapa overhead, ini tidak masalah. Kompiler hari ini harus dapat mengkompilasi kode Anda ke dalam bahasa mesin yang efisien sehingga kode yang dihasilkan efisien untuk arsitektur target. Gunakan sakelar optimisasi kompiler Anda untuk mendapatkan kode yang efisien.

Uwe Plonus
sumber
5
Saya akan mengatakan bahwa kita harus menulis program sedemikian rupa sehingga orang lain akan memahaminya.
Bartlomiej Lewandowski
Orang pertama yang harus membaca suatu program adalah pengembang sendiri. Itulah alasan mengapa saya menulis surat kepada Anda . Jika orang lain dapat membaca program ini juga bagus tetapi (di mata saya) tidak perlu (di tempat pertama). Jika Anda bekerja dalam tim, maka orang lain juga harus memahami program tersebut tetapi maksud saya adalah orang itu harus membaca suatu program, bukan komputer.
Uwe Plonus
5

Biasanya ketika Anda akan memiliki fungsi besar dan Anda membaginya menjadi banyak yang lebih kecil, yang lebih kecil ini akan digarisbawahi karena satu-satunya downside inlining (mengulangi instruksi yang sama terlalu banyak) tidak relevan dalam kasus ini. Itu berarti kode Anda akan bertindak seolah-olah Anda telah menulis satu fungsi besar.

Jika mereka tidak diuraikan karena alasan tertentu dan ini menjadi masalah kinerja, maka Anda harus mempertimbangkan inlining manual. Tidak semua aplikasi adalah formulir CRUD jaringan dengan latensi intrinsik yang besar.

Esailija
sumber
2

Mungkin tidak ada biaya perhitungan. Biasanya kompiler / JIT selama 10-20 tahun terakhir atau lebih berkaitan dengan fungsi inlining dengan sangat baik. Untuk C / C ++ biasanya terbatas pada fungsi 'inlinable' (yaitu definisi fungsi tersedia untuk kompiler selama kompilasi - yaitu di header file yang sama) tetapi teknik KPP saat ini mengatasinya.

Jika Anda harus menghabiskan waktu untuk optimasi tergantung pada area yang sedang Anda kerjakan. Jika Anda berurusan dengan aplikasi 'normal' yang menghabiskan sebagian besar waktu menunggu input - mungkin Anda tidak perlu khawatir tentang optimasi kecuali aplikasi 'terasa' lambat.

Bahkan dalam kasus seperti itu Anda harus berkonsentrasi pada banyak hal sebelum melakukan optimasi mikro:

  • Di mana masalahnya? Biasanya orang kesulitan menemukan hotspot saat kita membaca kode sumber secara berbeda. Kami memiliki rasio waktu operasi yang berbeda dan kami melakukannya secara berurutan sementara prosesor modern tidak .
  • Apakah perlu melakukan perhitungan setiap waktu? Misalnya jika Anda mengubah satu parameter dari ribuan, Anda mungkin ingin menghitung hanya bagian yang terpengaruh daripada keseluruhan model.
  • Apakah Anda menggunakan algoritma optimal? Ubah dari O(n)menjadi O(log n)memiliki dampak yang jauh lebih besar daripada apa pun yang dapat Anda capai dengan optimasi mikro.
  • Apakah Anda menggunakan struktur yang tepat? Katakanlah Anda menggunakan Listsaat Anda membutuhkan HashSetsehingga Anda memiliki O(n)pencarian saat Anda bisa melakukannya O(1).
  • Apakah Anda menggunakan paralelisme secara efisien? Saat ini bahkan ponsel dapat memiliki 4 core atau lebih, mungkin tergoda untuk menggunakan utas. Namun mereka bukan peluru perak karena mereka memiliki biaya sinkronisasi (tidak menyebutkan bahwa jika masalah adalah memori terikat mereka tetap tidak masuk akal).

Bahkan jika Anda memutuskan bahwa Anda perlu untuk melakukan mikro-optimasi (yang secara praktis berarti bahwa perangkat lunak Anda digunakan dalam HPC, tertanam atau hanya digunakan oleh sangat banyak orang - jika biaya tambahan pemeliharaan mengatasi biaya waktu komputer) Anda perlu untuk mengidentifikasi hotspot (kernel) yang ingin Anda percepat. Tapi Anda mungkin harus:

  1. Tahu persis platform yang sedang Anda kerjakan
  2. Ketahui persis kompiler yang sedang Anda kerjakan dan pengoptimalan apa yang mampu dilakukannya dan cara menulis kode idiomatik untuk mengaktifkan pengoptimalan tersebut
  3. Pikirkan tentang pola akses memori dan berapa banyak yang bisa Anda masukkan ke dalam cache (ukuran persisnya yang Anda ketahui dari poin 1).
  4. Kemudian jika Anda terikat terikat berpikir tentang mengatur ulang komputasi untuk menyimpan perhitungan

Sebagai ucapan terakhir. Biasanya satu-satunya masalah yang Anda miliki dengan pemanggilan metode adalah lompatan tidak langsung (metode virtual) yang tidak diprediksi oleh prediktor cabang (sayangnya lompatan tidak langsung adalah kasus yang sulit untuk itu). Namun:

  • Java memiliki JIT yang dalam banyak kasus dapat memprediksi jenis kelas dan karenanya target melompat sebelumnya dan karenanya dalam hotspot Anda seharusnya tidak memiliki banyak masalah.
  • Kompiler C ++ sering melakukan analisis program dan setidaknya dalam beberapa kasus dapat memprediksi target pada waktu kompilasi.
  • Dalam kedua kasus ketika target telah diprediksi, inlining harus bekerja. Jika kompiler tidak dapat melakukan peluang inlining, kami juga tidak bisa.
Maciej Piechotka
sumber
0

Jawaban saya mungkin tidak akan berkembang terlalu luas pada jawaban yang ada, tetapi saya merasa bahwa dua sen saya mungkin membantu.

Pertama; ya, untuk modularitas, Anda biasanya memberikan beberapa tingkat waktu eksekusi. Menulis segala sesuatu dalam kode rakitan akan memberi Anda kecepatan terbaik. Yang mengatakan ...

Anda kenal YouTube? Kemungkinan situs dengan bandwidth paling tinggi yang ada, atau yang kedua setelah Netflix? Mereka menulis sebagian besar kode mereka dalam Python, yang merupakan bahasa yang sangat modular tidak cukup dibangun untuk kinerja terbaik.

Masalahnya adalah, ketika ada sesuatu yang salah, dan pengguna mengeluh tentang memuat video lambat, tidak ada banyak skenario di mana kelambatan itu pada akhirnya akan dikaitkan dengan lambatnya kecepatan eksekusi Python. Namun, kompilasi ulang Python yang cepat, dan kemampuan modularnya untuk mencoba hal-hal baru tanpa pengecekan tipe mungkin akan memungkinkan para insinyur untuk men-debug apa yang salah dengan cukup cepat ("Wow. Magang baru kami menulis sebuah loop yang melakukan sub-query SQL baru untuk hasil SETIAP. ") atau (" Oh, Firefox telah mencabut format header caching yang lama; dan mereka membuat perpustakaan Python untuk mengatur yang baru dengan mudah ")

Dalam hal itu, bahkan dalam hal waktu eksekusi, bahasa modular dapat dianggap lebih cepat karena setelah Anda menemukan apa yang menjadi hambatan Anda, itu akan menjadi lebih mudah untuk mengatur ulang kode Anda untuk membuatnya bekerja dengan cara terbaik. Begitu banyak insinyur akan memberi tahu Anda bahwa kinerja yang hebat tidak berada di tempat yang mereka kira (dan sebenarnya hal-hal yang mereka optimalkan hampir tidak diperlukan; atau, bahkan tidak bekerja seperti yang mereka harapkan!)

Katana314
sumber
0

Iya dan tidak. Seperti yang telah dicatat program lain untuk keterbacaan pertama, kemudian untuk efisiensi. Namun, ada praktik standar yang dapat dibaca dan juga efisiensi. Sebagian besar kode dijalankan agak jarang, dan Anda tidak akan mendapatkan banyak keuntungan dari mengoptimalkannya.

Java bisa sebaris panggilan fungsi yang lebih kecil, jadi ada sedikit alasan untuk menghindari menulis fungsi. Pengoptimal cenderung bekerja lebih baik dengan kode yang lebih mudah dibaca. Ada penelitian yang menunjukkan cara pintas yang secara teoritis harus berjalan lebih cepat, sebenarnya membutuhkan waktu lebih lama. Kompiler JIT cenderung bekerja lebih baik kode lebih kecil dan potongan yang sering dijalankan dapat diidentifikasi dan dioptimalkan. Saya belum mencobanya tetapi saya akan mengharapkan satu fungsi besar yang relatif jarang dipanggil untuk tidak dikompilasi.

Ini mungkin tidak berlaku untuk Java, tetapi satu studi menemukan fungsi yang lebih besar benar-benar berjalan lebih lambat karena memerlukan model referensi memori yang berbeda. Ini khusus perangkat keras dan pengoptimal. Untuk modul yang lebih kecil digunakan instruksi yang berfungsi dalam halaman memori. Ini lebih cepat dan lebih kecil dari instruksi yang diperlukan ketika fungsi tidak sesuai dengan halaman.

Ada beberapa kasus di mana ada baiknya untuk mengoptimalkan kode, tetapi umumnya Anda perlu profil kode untuk menentukan di mana itu. Saya sering menemukan itu bukan kode yang saya harapkan.

BillThor
sumber