Dalam desain kelas saya, saya menggunakan kelas abstrak dan fungsi virtual secara luas. Saya merasa bahwa fungsi virtual mempengaruhi kinerja. Apakah ini benar? Tapi saya pikir perbedaan kinerja ini tidak terlihat dan sepertinya saya melakukan optimasi prematur. Baik?
c++
performance
optimization
virtual-functions
Navaneeth KN
sumber
sumber
Jawaban:
Aturan praktis yang baik adalah:
Penggunaan fungsi virtual akan memiliki efek yang sangat kecil pada kinerja, tetapi itu tidak akan mempengaruhi kinerja keseluruhan aplikasi Anda. Tempat yang lebih baik untuk mencari peningkatan kinerja adalah dalam algoritme dan I / O.
Artikel yang luar biasa yang membahas tentang fungsi virtual (dan lebih banyak lagi) adalah Pointer Fungsi Anggota dan Delegasi C ++ yang Tercepat Mungkin .
sumber
Pertanyaan Anda membuat saya penasaran, jadi saya melanjutkan dan menjalankan beberapa pengaturan waktu pada CPU PowerPC berurutan 3GHz yang bekerja dengan kami. Tes yang saya jalankan adalah membuat kelas vektor 4d sederhana dengan fungsi get / set
Lalu saya mengatur tiga array masing-masing berisi 1024 vektor ini (cukup kecil untuk muat di L1) dan menjalankan loop yang menambahkannya satu sama lain (Ax = Bx + Cx) 1000 kali. Aku berlari ini dengan fungsi didefinisikan sebagai
inline
,virtual
, dan panggilan fungsi biasa. Inilah hasilnya:Jadi, dalam hal ini (di mana semuanya sesuai dalam cache) panggilan fungsi virtual sekitar 20x lebih lambat dari panggilan inline. Tapi apa maksudnya ini? Setiap perjalanan melalui loop disebabkan tepat
3 * 4 * 1024 = 12,288
panggilan fungsi dengan (1024 vektor kali empat komponen kali tiga panggilan per penambahan), jadi kali ini mewakili1000 * 12,288 = 12,288,000
panggilan fungsi. Virtual loop memakan waktu 92ms lebih lama dari loop langsung, sehingga overhead tambahan per panggilan adalah 7 nanodetik per fungsi.Dari sini saya menyimpulkan: ya , fungsi virtual jauh lebih lambat daripada fungsi langsung, dan tidak , kecuali Anda berencana memanggilnya sepuluh juta kali per detik, itu tidak masalah.
Lihat juga: perbandingan rakitan yang dihasilkan.
sumber
Ketika Objective-C (di mana semua metode virtual) adalah bahasa utama untuk iPhone dan Java freakin adalah bahasa utama untuk Android, saya pikir cukup aman untuk menggunakan fungsi virtual C ++ pada menara dual-core 3 GHz kami.
sumber
Dalam aplikasi yang sangat kritis kinerja (seperti video game) panggilan fungsi virtual bisa terlalu lambat. Dengan perangkat keras modern, kekhawatiran kinerja terbesar adalah cache miss. Jika data tidak ada dalam cache, mungkin ratusan siklus sebelum tersedia.
Panggilan fungsi normal dapat menghasilkan cache instruksi yang hilang ketika CPU mengambil instruksi pertama dari fungsi yang baru dan itu tidak ada dalam cache.
Panggilan fungsi virtual terlebih dahulu perlu memuat pointer vtable dari objek. Ini dapat menyebabkan kehilangan cache data. Kemudian itu memuat pointer fungsi dari vtable yang dapat mengakibatkan kehilangan cache data lain. Kemudian ia memanggil fungsi yang dapat mengakibatkan cache instruksi ketinggalan seperti fungsi non-virtual.
Dalam banyak kasus, dua kesalahan cache tambahan tidak menjadi masalah, tetapi dalam loop ketat pada kode kritis kinerja, ini dapat secara dramatis mengurangi kinerja.
sumber
Dari halaman 44 dari manual "Mengoptimalkan Perangkat Lunak dalam C ++" Agner Fog :
sumber
switch
. Dengancase
nilai yang sepenuhnya arbitrer , tentu saja. Tetapi jika semuacase
berurutan, kompiler mungkin dapat mengoptimalkan ini menjadi lompatan-tabel (ah, yang mengingatkan saya pada hari-hari Z80 yang baik), yang seharusnya (untuk jangka waktu yang lebih baik) waktu-konstan. Bukannya saya sarankan mencoba mengganti vfuncs denganswitch
, yang menggelikan. ;)benar. Itu masalah jalan kembali ketika komputer berjalan pada 100Mhz, karena setiap panggilan metode memerlukan pencarian pada vtable sebelum dipanggil. Tapi hari ini .. pada CPU 3Ghz yang memiliki cache level 1 dengan lebih banyak memori daripada komputer pertama saya? Tidak semuanya. Mengalokasikan memori dari RAM utama akan menghabiskan lebih banyak waktu daripada jika semua fungsi Anda virtual.
Ini seperti masa lalu, di mana orang mengatakan pemrograman terstruktur lambat karena semua kode dipecah menjadi fungsi, setiap fungsi memerlukan alokasi stack dan pemanggilan fungsi!
Satu-satunya waktu saya bahkan berpikir untuk mempertimbangkan dampak kinerja dari fungsi virtual, adalah jika itu sangat banyak digunakan dan dipakai dalam kode templated yang berakhir di seluruh segalanya. Meski begitu, saya tidak akan menghabiskan terlalu banyak upaya untuk itu!
PS memikirkan bahasa 'mudah digunakan' lainnya - semua metode mereka virtual di bawah selimut dan mereka tidak merangkak saat ini.
sumber
Ada kriteria kinerja lain selain waktu eksekusi. Vtable juga memakan ruang memori, dan dalam beberapa kasus dapat dihindari: ATL menggunakan waktu kompilasi " simulasi ikatan dinamis " dengan templatuntuk mendapatkan efek "polimorfisme statis", yang agak sulit dijelaskan; Anda pada dasarnya meneruskan kelas turunan sebagai parameter ke templat kelas dasar, jadi pada waktu kompilasi, kelas dasar "tahu" apa kelas turunannya dalam setiap instance. Tidak akan membiarkan Anda menyimpan beberapa kelas turunan yang berbeda dalam kumpulan tipe dasar (itu run-time polymorphism) tetapi dari pengertian statis, jika Anda ingin membuat kelas Y yang sama dengan templat kelas X yang sudah ada sebelumnya yang memiliki kait untuk jenis overriding ini, Anda hanya perlu menimpa metode yang Anda pedulikan, dan kemudian Anda mendapatkan metode dasar kelas X tanpa harus memiliki vtable.
Di kelas dengan jejak memori yang besar, biaya satu pointer vtable tidak banyak, tetapi beberapa kelas ATL di COM sangat kecil, dan itu layak penghematan vtable jika kasus run-time polimorfisme tidak akan pernah terjadi.
Lihat juga pertanyaan SO lainnya ini .
Ngomong-ngomong, inilah postingan yang saya temukan yang berbicara tentang aspek kinerja waktu CPU.
sumber
Ya, Anda benar dan jika Anda ingin tahu tentang biaya panggilan fungsi virtual Anda mungkin menemukan posting ini menarik.
sumber
Satu-satunya cara saya dapat melihat bahwa fungsi virtual akan menjadi masalah kinerja adalah jika banyak fungsi virtual dipanggil dalam loop ketat, dan jika dan hanya jika mereka menyebabkan kesalahan halaman atau operasi memori "berat" lainnya terjadi.
Meskipun seperti orang lain katakan itu cukup banyak tidak akan menjadi masalah bagi Anda dalam kehidupan nyata. Dan jika menurut Anda, jalankan profiler, lakukan beberapa tes, dan verifikasi apakah ini benar-benar masalah sebelum mencoba "mendesain" kode Anda untuk keuntungan kinerja.
sumber
Ketika metode kelas bukan virtual, kompiler biasanya melakukan in-lining. Sebaliknya, ketika Anda menggunakan pointer ke beberapa kelas dengan fungsi virtual, alamat sebenarnya hanya akan diketahui saat runtime.
Ini diilustrasikan dengan baik oleh tes, perbedaan waktu ~ 700% (!):
Dampak panggilan fungsi virtual sangat tergantung pada situasi. Jika ada beberapa panggilan dan sejumlah besar pekerjaan di dalam fungsi - itu bisa diabaikan.
Atau, ketika itu adalah panggilan virtual berulang kali digunakan berkali-kali, sambil melakukan beberapa operasi sederhana - itu bisa sangat besar.
sumber
++ia
. Terus?Saya telah bolak-balik pada ini setidaknya 20 kali pada proyek khusus saya. Meskipun ada bisa ada beberapa keuntungan besar dalam hal penggunaan kembali kode, kejelasan, rawatan, dan mudah dibaca, di sisi lain, kinerja hit masih melakukan eksis dengan fungsi virtual.
Apakah hit kinerja akan terlihat pada laptop / desktop / tablet modern ... mungkin tidak! Namun, dalam kasus tertentu dengan sistem tertanam, hit kinerja mungkin menjadi faktor pendorong inefisiensi kode Anda, terutama jika fungsi virtual dipanggil berulang-ulang dalam satu lingkaran.
Berikut adalah makalah tertanggal yang menjelaskan praktik terbaik untuk C / C ++ dalam konteks sistem tertanam: http://www.open-std.org/jtc1/sc22/wg21/docs/ESC_Boston_01_304_paper.pdf
Untuk menyimpulkan: tergantung pada programmer untuk memahami pro / kontra dari menggunakan konstruksi tertentu atas yang lain. Kecuali jika Anda didorong oleh kinerja super, Anda mungkin tidak peduli dengan hit kinerja dan harus menggunakan semua hal-hal OO yang rapi di C ++ untuk membantu membuat kode Anda dapat digunakan sebaik mungkin.
sumber
Dalam pengalaman saya, hal yang relevan utama adalah kemampuan untuk sebaris fungsi. Jika Anda memiliki kebutuhan kinerja / pengoptimalan yang menentukan suatu fungsi perlu diuraikan, maka Anda tidak dapat membuat fungsi tersebut virtual karena akan mencegahnya. Kalau tidak, Anda mungkin tidak akan melihat perbedaannya.
sumber
Satu hal yang perlu diperhatikan adalah bahwa ini:
mungkin lebih cepat dari ini:
Ini karena metode pertama hanya memanggil satu fungsi sementara yang kedua mungkin memanggil banyak fungsi yang berbeda. Ini berlaku untuk fungsi virtual apa pun dalam bahasa apa pun.
Saya katakan "boleh" karena ini tergantung pada kompiler, cache, dll.
sumber
Hukuman kinerja menggunakan fungsi virtual tidak akan pernah bisa mengalahkan kelebihan yang Anda dapatkan di tingkat desain. Seharusnya panggilan ke fungsi virtual akan 25% kurang efisien daripada panggilan langsung ke fungsi statis. Ini karena ada tingkat tipuan melalui VMT. Namun waktu yang dibutuhkan untuk melakukan panggilan biasanya sangat kecil dibandingkan dengan waktu yang dibutuhkan dalam pelaksanaan fungsi Anda yang sebenarnya sehingga total biaya kinerja tidak dapat diabaikan, terutama dengan kinerja perangkat keras saat ini. Lebih jauh lagi, kompiler terkadang dapat mengoptimalkan dan melihat bahwa tidak ada panggilan virtual yang diperlukan dan mengkompilasinya menjadi panggilan statis. Jadi jangan khawatir gunakan fungsi virtual dan kelas abstrak sebanyak yang Anda butuhkan.
sumber
The performance penalty of using virtual functions can sometimes be so insignificant that it is completely outweighed by the advantages you get at the design level.
Perbedaan utama dikatakansometimes
, bukannever
.Saya selalu mempertanyakan hal ini pada diri saya sendiri, terutama karena - beberapa tahun yang lalu - Saya juga melakukan tes seperti membandingkan waktu panggilan metode anggota standar dengan yang virtual dan benar-benar marah tentang hasilnya pada waktu itu, memiliki panggilan virtual kosong menjadi 8 kali lebih lambat daripada non-virtual.
Hari ini saya harus memutuskan apakah akan menggunakan fungsi virtual atau tidak untuk mengalokasikan lebih banyak memori di kelas buffer saya, dalam aplikasi yang sangat kritis kinerja, jadi saya googled (dan menemukan Anda), dan pada akhirnya, melakukan tes lagi.
Dan sangat terkejut bahwa itu - pada kenyataannya - benar-benar tidak penting lagi. Meskipun masuk akal untuk memiliki inline lebih cepat daripada non-virtual, dan mereka lebih cepat daripada virtual, sering terjadi pada beban komputer secara keseluruhan, apakah cache Anda memiliki data yang diperlukan atau tidak, dan sementara Anda mungkin dapat mengoptimalkan pada tingkat cache, saya pikir, bahwa ini harus dilakukan oleh pengembang kompiler lebih dari oleh pengembang aplikasi.
sumber