Apakah membuat objek menggunakan refleksi daripada memanggil konstruktor kelas menghasilkan perbedaan kinerja yang signifikan?
java
performance
optimization
reflection
dmanxiii
sumber
sumber
Jawaban:
Ya, tentu saja. Melihat ke atas kelas melalui refleksi adalah, dengan besarnya , lebih mahal.
Mengutip dokumentasi Java tentang refleksi :
Inilah tes sederhana yang saya peretas dalam 5 menit pada mesin saya, menjalankan Sun JRE 6u10:
Dengan hasil ini:
Ingatlah pencarian dan instantiasi dilakukan bersama-sama, dan dalam beberapa kasus pencarian dapat dihilangkan, tetapi ini hanyalah contoh dasar.
Bahkan jika Anda hanya instantiate, Anda masih mendapatkan kinerja yang baik:
Sekali lagi, YMMV.
sumber
Ya, ini lebih lambat.
Tapi ingat aturan # 1 sialan - OPTIMASI PREMATUR ADALAH ROOT DARI SEMUA JAHAT
(Yah, mungkin diikat dengan # 1 untuk KERING)
Aku bersumpah, jika seseorang mendatangiku di tempat kerja dan menanyakan ini padaku, aku akan sangat berhati-hati terhadap kode mereka selama beberapa bulan ke depan.
Anda tidak boleh mengoptimalkan sampai Anda yakin Anda membutuhkannya, sampai saat itu, cukup tulis kode yang baik dan mudah dibaca.
Oh, dan aku juga tidak bermaksud menulis kode bodoh. Hanya memikirkan cara terbersih yang dapat Anda lakukan - tidak ada salin dan tempel, dll. (Tetap waspada terhadap hal-hal seperti loop batin dan menggunakan koleksi yang paling sesuai dengan kebutuhan Anda - Mengabaikan ini bukan pemrograman "tidak dioptimalkan" , ini pemrograman "buruk")
Ini membuat saya takut ketika saya mendengar pertanyaan seperti ini, tetapi kemudian saya lupa bahwa setiap orang harus mempelajari semua peraturan sendiri sebelum mereka benar-benar mendapatkannya. Anda akan mendapatkannya setelah menghabiskan satu bulan bulan men-debug sesuatu yang "Dioptimalkan" oleh seseorang.
EDIT:
Hal yang menarik terjadi di utas ini. Periksa jawaban # 1, ini adalah contoh seberapa kuat kompiler dalam mengoptimalkan berbagai hal. Tes ini sepenuhnya tidak valid karena instantiasi non-reflektif dapat sepenuhnya difaktorkan.
Pelajaran? Jangan pernah optimalkan sampai Anda telah menulis solusi kode yang bersih dan rapi dan membuktikannya terlalu lambat.
sumber
Anda mungkin menemukan bahwa A a = new A () sedang dioptimalkan oleh JVM. Jika Anda meletakkan objek ke dalam array, mereka tidak berkinerja baik. ;) Cetakan berikut ini ...
Ini menunjukkan perbedaannya adalah sekitar 150 ns pada mesin saya.
sumber
Class.getDeclaredMethod
) dan kemudian meneleponMethod.invoke
beberapa kali? Apakah saya menggunakan refleksi sekali atau sebanyak yang saya minta? Pertanyaan lanjutan, bagaimana jika alih-alihMethod
itu adalahConstructor
dan saya lakukanConstructor.newInstance
beberapa kali?Jika memang ada kebutuhan untuk sesuatu yang lebih cepat daripada refleksi, dan itu bukan hanya optimasi prematur, maka bytecode generasi dengan ASM atau perpustakaan tingkat yang lebih tinggi adalah sebuah pilihan. Menghasilkan bytecode pertama kali lebih lambat daripada hanya menggunakan refleksi, tetapi begitu bytecode dibuat, itu secepat kode Java normal dan akan dioptimalkan oleh kompiler JIT.
Beberapa contoh aplikasi yang menggunakan pembuatan kode:
Meminta metode pada proksi yang dihasilkan oleh CGLIB sedikit lebih cepat dari proksi dinamis Java , karena CGLIB menghasilkan bytecode untuk prokinya, tetapi proksi dinamis hanya menggunakan refleksi ( saya mengukur CGLIB sekitar 10x lebih cepat dalam pemanggilan metode, tetapi membuat proksi lebih lambat).
JSerial menghasilkan bytecode untuk membaca / menulis bidang objek serial, alih-alih menggunakan refleksi. Ada beberapa tolok ukur di situs JSerial.
Saya tidak 100% yakin (dan saya tidak ingin membaca sumbernya sekarang), tetapi saya pikir Guice menghasilkan bytecode untuk melakukan injeksi ketergantungan. Koreksi saya jika saya salah.
sumber
"Signifikan" sepenuhnya tergantung pada konteks.
Jika Anda menggunakan refleksi untuk membuat objek handler tunggal berdasarkan pada beberapa file konfigurasi, dan kemudian menghabiskan sisa waktu Anda menjalankan query database, maka itu tidak signifikan. Jika Anda membuat banyak objek melalui refleksi dalam satu lingkaran ketat, maka ya, itu penting.
Secara umum, fleksibilitas desain (jika diperlukan!) Harus mendorong Anda menggunakan refleksi, bukan kinerja. Namun, untuk menentukan apakah kinerja merupakan masalah, Anda perlu membuat profil alih-alih mendapatkan respons sewenang-wenang dari forum diskusi.
sumber
Ada beberapa overhead dengan refleksi, tetapi jauh lebih kecil pada VM modern daripada sebelumnya.
Jika Anda menggunakan refleksi untuk membuat setiap objek sederhana di program Anda, maka ada sesuatu yang salah. Menggunakannya sesekali, ketika Anda memiliki alasan yang baik, seharusnya tidak menjadi masalah sama sekali.
sumber
Ya ada hit kinerja saat menggunakan Refleksi tetapi solusi yang mungkin untuk optimasi adalah dengan metode caching:
akan menghasilkan:
[java] Metode panggilan 10.000 kali secara refleks dengan pencarian membutuhkan 5.618 mili
[java] Metode pemanggilan 1000000 kali secara refleks dengan cache membutuhkan 270 millis
sumber
Refleksi lambat, meskipun alokasi objek tidak seputus harapannya seperti aspek refleksi lainnya. Untuk mencapai kinerja yang setara dengan instantiasi berbasis refleksi mengharuskan Anda untuk menulis kode Anda sehingga jit dapat mengetahui kelas mana yang sedang dipakai. Jika identitas kelas tidak dapat ditentukan, maka kode alokasi tidak dapat digarisbawahi. Lebih buruk, analisis pelarian gagal, dan objek tidak dapat dialokasikan stack. Jika Anda beruntung, profil run-time JVM mungkin datang untuk menyelamatkan jika kode ini menjadi panas, dan dapat menentukan secara dinamis kelas mana yang mendominasi dan dapat mengoptimalkan untuk yang itu.
Ketahuilah bahwa microbenchmark pada utas ini sangat cacat, jadi bawalah mereka dengan sebutir garam. Yang paling tidak cacat sejauh ini adalah Peter Lawrey: ia melakukan pemanasan untuk mendapatkan metode yang tepat, dan (secara sadar) mengalahkan analisis pelarian untuk memastikan alokasi benar-benar terjadi. Meskipun ada masalah, misalnya: misalnya, sejumlah besar toko array dapat diperkirakan mengalahkan cache dan menyimpan buffer, jadi ini akan menjadi benchmark memori jika alokasi Anda sangat cepat. (Kudos kepada Peter karena mendapatkan kesimpulan yang tepat: bahwa perbedaannya adalah "150ns" daripada "2.5x". Saya curiga dia melakukan hal semacam ini untuk mencari nafkah.)
sumber
Cukup menarik, dengan menetapkan setAccessible (true), yang melewatkan pemeriksaan keamanan, memiliki pengurangan 20% dalam biaya.
Tanpa setAccessible (true)
Dengan setAccessible (true)
sumber
1000000
doa?setAccessible()
dapat memiliki banyak perbedaan secara umum, terutama untuk metode dengan banyak argumen, jadi harus selalu disebut.Ya, ini jauh lebih lambat. Kami menjalankan beberapa kode yang melakukan itu, dan sementara saya tidak memiliki metrik yang tersedia saat ini, hasil akhirnya adalah kami harus memperbaiki kode itu untuk tidak menggunakan refleksi. Jika Anda tahu apa kelasnya, cukup hubungi konstruktor secara langsung.
sumber
Dalam doReflection () adalah overhead karena Class.forName ("misc.A") (yang akan membutuhkan pencarian kelas, berpotensi memindai jalur kelas pada filsystem), daripada newInstance () dipanggil pada kelas. Saya bertanya-tanya seperti apa statistiknya jika Class.forName ("misc.A") dilakukan hanya sekali di luar for-loop, itu tidak benar-benar harus dilakukan untuk setiap doa dari loop.
sumber
Ya, selalu akan lebih lambat membuat objek dengan refleksi karena JVM tidak dapat mengoptimalkan kode pada waktu kompilasi. Lihat tutorial Sun / Java Reflection untuk lebih jelasnya.
Lihat tes sederhana ini:
sumber
Class.forName()
) dari instanciation (newInstance ()), karena mereka bervariasi secara signifikan dalam karakteristik kinerja mereka dan Anda kadang-kadang dapat menghindari pencarian berulang dalam sistem yang dirancang dengan baik.Seringkali Anda dapat menggunakan Apache commons BeanUtils atau PropertyUtils yang melakukan introspeksi (pada dasarnya mereka men-cache data meta tentang kelas sehingga mereka tidak selalu perlu menggunakan refleksi).
sumber
Saya pikir itu tergantung pada seberapa ringan / berat metode targetnya. jika metode target sangat ringan (mis. pengambil / penyetel), itu bisa 1 ~ 3 kali lebih lambat. jika metode target memakan waktu sekitar 1 milidetik atau lebih, maka kinerjanya akan sangat dekat. di sini adalah tes yang saya lakukan dengan Java 8 dan reflectasm :
Kode uji lengkap tersedia di GitHub: ReflectionTest.java
sumber