Saya mengerjakan aplikasi dan satu pendekatan desain melibatkan penggunaan instanceof
operator yang sangat berat . Meskipun saya tahu bahwa desain OO umumnya mencoba untuk tidak menggunakan instanceof
, itu adalah cerita yang berbeda dan pertanyaan ini murni terkait dengan kinerja. Saya bertanya-tanya apakah ada dampak kinerja? Apakah secepat ==
?
Sebagai contoh, saya memiliki kelas dasar dengan 10 subclass. Dalam satu fungsi yang mengambil kelas dasar, saya memeriksa apakah kelas tersebut adalah turunan dari subkelas dan menjalankan beberapa rutin.
Salah satu cara lain yang saya pikirkan untuk menyelesaikannya adalah dengan menggunakan primitif "type id" integer, dan menggunakan bitmask untuk mewakili kategori dari subclass, dan kemudian hanya melakukan sedikit perbandingan topeng dari subclass "type id" ke topeng konstan mewakili kategori.
Apakah instanceof
entah bagaimana dioptimalkan oleh JVM menjadi lebih cepat dari itu? Saya ingin tetap menggunakan Java tetapi kinerja aplikasi sangat penting. Akan sangat keren jika seseorang yang telah menyusuri jalan ini sebelumnya dapat menawarkan beberapa saran. Apakah saya terlalu banyak melakukan nitpicking atau berfokus pada hal yang salah untuk dioptimalkan?
sumber
Jawaban:
Kompiler JVM / JIC modern telah menghilangkan hit kinerja sebagian besar operasi "lambat" tradisional, termasuk instance, penanganan pengecualian, refleksi, dll.
Seperti yang ditulis Donald Knuth, "Kita harus melupakan efisiensi kecil, katakanlah sekitar 97% dari waktu: optimasi prematur adalah akar dari semua kejahatan." Kinerja instanceof mungkin tidak akan menjadi masalah, jadi jangan buang waktu Anda untuk mencari solusi eksotis sampai Anda yakin itu masalahnya.
sumber
try { ObjT o = (ObjT)object } catch (e) { no not one of these }
akan lebih cepat lebih lambat ??Pendekatan
Saya menulis program benchmark untuk mengevaluasi implementasi yang berbeda:
instanceof
implementasi (sebagai referensi)@Override
metode pengujiangetClass() == _.class
penerapanSaya menggunakan jmh untuk menjalankan benchmark dengan 100 panggilan pemanasan, 1000 iterasi di bawah pengukuran, dan dengan 10 fork. Jadi setiap opsi diukur dengan 10.000 kali, yang membutuhkan 12:18:57 untuk menjalankan seluruh tolok ukur pada MacBook Pro saya dengan macOS 10.12.4 dan Java 1.8. Benchmark mengukur waktu rata-rata setiap opsi. Untuk lebih jelasnya lihat implementasi saya di GitHub .
Demi kelengkapan: Ada versi sebelumnya dari jawaban ini dan tolok ukur saya .
Hasil
tl; dr
Di Jawa 1.8
instanceof
adalah pendekatan tercepat, meskipungetClass()
sangat dekat.sumber
+0.(9)
untuk sains!+1.0(9)
. :)System.currentTimeMillis()
pada operasi yang tidak lebih dari pemanggilan metode tunggal, yang harus memberikan banyak presisi rendah. Gunakan kerangka kerja benchmark seperti JMH sebagai gantinya!Saya baru saja membuat tes sederhana untuk melihat bagaimana kinerja instanceOf dibandingkan dengan panggilan s.equals () sederhana ke objek string dengan hanya satu huruf.
dalam loop 10.000.000 instanceOf memberi saya 63-96ms, dan string yang sama memberi saya 106-230ms
Saya menggunakan java jvm 6.
Jadi dalam tes sederhana saya lebih cepat untuk melakukan instanceOf daripada perbandingan string satu karakter.
menggunakan Integer .equals () alih-alih string memberi saya hasil yang sama, hanya ketika saya menggunakan == saya lebih cepat daripada instanceOf oleh 20ms (dalam 10.000.000 loop)
sumber
equals()
tidak akan memotongnya, karena subkelas; kamu membutuhkanisAssignableFrom()
.Item yang akan menentukan dampak kinerja adalah:
Saya membuat microbenchmark untuk empat metode pengiriman yang berbeda . Hasil dari Solaris adalah sebagai berikut, dengan jumlah yang lebih kecil lebih cepat:
sumber
Menjawab pertanyaan terakhir Anda: Kecuali seorang profiler memberi tahu Anda, bahwa Anda menghabiskan jumlah waktu yang konyol dalam contoh: Ya, Anda benar-benar gila.
Sebelum bertanya-tanya tentang mengoptimalkan sesuatu yang tidak perlu dioptimalkan: Tulis algoritma Anda dengan cara yang paling mudah dibaca dan jalankan. Jalankan, sampai jit-compiler mendapat kesempatan untuk mengoptimalkannya sendiri. Jika kemudian Anda memiliki masalah dengan kode ini, gunakan profiler untuk memberi tahu Anda, di mana mendapatkan yang terbaik dan optimalkan ini.
Pada saat kompiler yang sangat optimal, tebakan Anda tentang kemacetan kemungkinan besar akan benar-benar salah.
Dan dalam semangat sejati dari jawaban ini (yang saya yakini dengan sepenuh hati): Saya benar-benar tidak tahu bagaimana instanceof dan == berhubungan ketika jit-compiler mendapat kesempatan untuk mengoptimalkannya.
Saya lupa: Jangan pernah mengukur putaran pertama.
sumber
Saya punya pertanyaan yang sama, tetapi karena saya tidak menemukan 'metrik kinerja' untuk use case yang mirip dengan milik saya, saya telah melakukan beberapa kode sampel lagi. Pada perangkat keras saya dan Java 6 & 7, perbedaan antara instanceof dan mengaktifkan iterasi 10mln adalah
Jadi, instanceof benar-benar lebih lambat, terutama pada sejumlah besar pernyataan if-else-if, namun perbedaan akan diabaikan dalam aplikasi nyata.
sumber
instanceof
sangat cepat, hanya mengambil beberapa instruksi CPU.Rupanya, jika suatu kelas
X
tidak memiliki subclass dimuat (JVM tahu),instanceof
dapat dioptimalkan sebagai:Biaya utama hanya untuk membaca!
Jika
X
memang ada subclass dimuat, diperlukan beberapa bacaan lagi; mereka kemungkinan terletak bersama sehingga biaya tambahan juga sangat rendah.Berita baik semuanya!
sumber
foo
- tetapi inifoo
benar-benar saat ini dioptimalkan oleh Oracle javac / VM - atau hanya mungkin bahwa ia akan melakukan itu di masa depan? Juga, saya bertanya kepada penjawab apakah dia memiliki sumber dukungan (baik itu dokumen, kode sumber, blog dev) yang mendokumentasikan bahwa itu memang dapat dioptimalkan atau dioptimalkan ? Tanpanya, jawaban ini hanyalah beberapa pemikiran acak tentang apa yang dapat dilakukan oleh kompiler .Instanceof sangat cepat. Itu bermuara pada bytecode yang digunakan untuk perbandingan referensi kelas. Coba beberapa juta instance dalam satu lingkaran dan lihat sendiri.
sumber
instanceof mungkin akan lebih mahal daripada yang sederajat dalam implementasi dunia nyata (yaitu, yang mana instanceof benar-benar dibutuhkan, dan Anda tidak bisa menyelesaikannya dengan mengganti metode umum, seperti setiap buku teks pemula serta Demian di atas menyarankan).
Mengapa demikian? Karena apa yang mungkin akan terjadi adalah bahwa Anda memiliki beberapa antarmuka, yang menyediakan beberapa fungsi (katakanlah, antarmuka x, y dan z), dan beberapa objek untuk memanipulasi yang mungkin (atau tidak) mengimplementasikan salah satu antarmuka tersebut ... tetapi tidak secara langsung. Katakanlah, misalnya, saya punya:
w meluas x
A mengimplementasikan w
B meluas A
C memanjang B, mengimplementasikan y
D meluas C, mengimplementasikan z
Misalkan saya sedang memproses instance D, objek d. Komputasi (d instanceof x) perlu mengambil d.getClass (), loop melalui antarmuka yang diterapkan untuk mengetahui apakah seseorang == ke x, dan jika tidak melakukannya lagi secara rekursif untuk semua leluhur mereka ... Dalam kasus kami, jika Anda melakukan penjelajahan luas pertama dari pohon itu, menghasilkan setidaknya 8 perbandingan, seandainya y dan z tidak memperpanjang apa pun ...
Kompleksitas pohon derivasi dunia nyata cenderung lebih tinggi. Dalam beberapa kasus, JIT dapat mengoptimalkan sebagian besar darinya, jika ia dapat menyelesaikan di muka d sebagai, dalam semua kasus yang mungkin, sebuah instance dari sesuatu yang memanjang x. Namun, secara realistis, Anda akan sering melewati jalur pohon itu.
Jika itu menjadi masalah, saya akan menyarankan menggunakan peta handler sebagai gantinya, menghubungkan kelas konkret objek ke penutupan yang melakukan penanganan. Ini menghilangkan fase traversal pohon yang mendukung pemetaan langsung. Namun, berhati-hatilah bahwa jika Anda telah menetapkan handler untuk C.class, objek saya d di atas tidak akan dikenali.
di sini adalah 2 sen saya, saya harap mereka membantu ...
sumber
instanceof sangat efisien, sehingga kinerja Anda tidak mungkin menderita. Namun, menggunakan banyak instanceof menunjukkan masalah desain.
Jika Anda dapat menggunakan xClass == String.class, ini lebih cepat. Catatan: Anda tidak perlu instanceof untuk kelas akhir.
sumber
x.getClass() == Class.class
ini sama denganx instanceof Class
x
demikiannull
. (Atau yang lebih jelas)Secara umum alasan mengapa operator "instanceof" disukai dalam kasus seperti itu (di mana instanceof sedang memeriksa untuk subclass dari kelas dasar ini) adalah karena apa yang harus Anda lakukan adalah memindahkan operasi ke dalam metode dan menimpanya untuk yang sesuai subkelas. Misalnya, jika Anda memiliki:
Anda dapat menggantinya dengan
dan kemudian memiliki implementasi "doEverything ()" di panggilan Class1 "doThis ()", dan di panggilan Class2 "doThat ()", dan seterusnya.
sumber
'instanceof' sebenarnya adalah operator, seperti + atau -, dan saya percaya bahwa ia memiliki instruksi bytecode JVM sendiri. Ini harusnya cepat.
Saya seharusnya tidak bahwa jika Anda memiliki saklar di mana Anda menguji apakah suatu objek adalah turunan dari beberapa subkelas, maka desain Anda mungkin perlu dikerjakan ulang. Pertimbangkan untuk mendorong perilaku khusus subkelas ke dalam subkelas itu sendiri.
sumber
Demian dan Paul menyebutkan poin yang bagus; namun , penempatan kode untuk dieksekusi benar-benar tergantung pada bagaimana Anda ingin menggunakan data ...
Saya penggemar berat objek data kecil yang dapat digunakan dalam banyak cara. Jika Anda mengikuti pendekatan override (polymorphic), objek Anda hanya dapat digunakan "satu arah".
Di sinilah pola masuk ...
Anda dapat menggunakan pengiriman ganda (seperti dalam pola pengunjung) untuk meminta setiap objek untuk "memanggil Anda" dengan sendirinya - ini akan menyelesaikan jenis objek. Namun (lagi) Anda membutuhkan kelas yang dapat "melakukan hal-hal" dengan semua subtipe yang mungkin.
Saya lebih suka menggunakan pola strategi, di mana Anda dapat mendaftarkan strategi untuk setiap subtipe yang ingin Anda tangani. Sesuatu seperti yang berikut ini. Perhatikan bahwa ini hanya membantu untuk pencocokan jenis yang tepat, tetapi memiliki keunggulan yang dapat diperpanjang - kontributor pihak ketiga dapat menambahkan jenis dan penangan mereka sendiri. (Ini bagus untuk kerangka kerja dinamis seperti OSGi, di mana bundel baru dapat ditambahkan)
Semoga ini akan menginspirasi beberapa ide lain ...
sumber
Saya menulis tes kinerja berdasarkan jmh-java-benchmark-archetype: 2.21. JDK adalah openjdk dan versinya 1.8.0_212. Mesin uji adalah mac pro. Hasil tes adalah:
Hasilnya menunjukkan bahwa: getClass lebih baik daripada instanceOf, yang bertentangan dengan tes lainnya. Namun, saya tidak tahu mengapa.
Kode tes di bawah ini:
sumber
Sulit untuk mengatakan bagaimana JVM tertentu mengimplementasikan instance, tetapi dalam kebanyakan kasus, Object dapat dibandingkan dengan struct dan kelas juga dan setiap struct objek memiliki pointer ke struct kelas itu adalah instance. Jadi sebenarnya contoh untuk
mungkin secepat kode C berikut
dengan asumsi kompiler JIT sudah ada dan melakukan pekerjaan yang layak.
Mempertimbangkan bahwa ini hanya mengakses sebuah pointer, mendapatkan sebuah pointer pada suatu titik offset tertentu pada pointer dan membandingkannya dengan pointer yang lain (yang pada dasarnya sama dengan pengujian ke angka 32 bit yang sama), saya akan mengatakan bahwa operasi dapat benar-benar sangat cepat.
Tidak harus, meskipun, itu sangat tergantung pada JVM. Namun, jika ini akan menjadi operasi bottleneck dalam kode Anda, saya akan menganggap implementasi JVM agak buruk. Bahkan yang tidak memiliki kompiler JIT dan hanya kode interpretasi yang dapat membuat instanceof tes dalam waktu singkat.
sumber
Saya akan kembali kepada Anda atas kinerja. Tetapi cara untuk menghindari masalah (atau ketiadaan) sama sekali adalah dengan membuat antarmuka induk untuk semua subclass di mana Anda perlu melakukan instanceof. Antarmuka akan menjadi super set semua metode dalam sub-kelas yang Anda perlu lakukan instance of check. Jika suatu metode tidak berlaku untuk sub-kelas tertentu, cukup sediakan implementasi tiruan dari metode ini. Jika saya tidak salah memahami masalah ini, ini adalah bagaimana saya menyelesaikan masalah di masa lalu.
sumber
InstanceOf adalah peringatan desain Object Oriented yang buruk.
JVM saat ini berarti instanceOf tidak terlalu mengkhawatirkan kinerja. Jika Anda mendapati diri Anda sering menggunakannya, terutama untuk fungsionalitas inti, mungkin inilah saatnya untuk melihat desainnya. Peningkatan kinerja (dan kesederhanaan / pemeliharaan) dari refactoring ke desain yang lebih baik akan jauh lebih besar daripada siklus prosesor aktual yang dihabiskan untuk panggilan instanceOf sebenarnya .
Untuk memberikan contoh pemrograman sederhana yang sangat kecil.
Apakah arsitektur yang buruk adalah pilihan yang lebih baik untuk memiliki SomeObject menjadi kelas induk dari dua kelas anak di mana setiap kelas anak menimpa metode (doSomething) sehingga kode akan terlihat seperti ini:
sumber
Dalam versi Java modern, instanceof operator lebih cepat sebagai panggilan metode sederhana. Ini berarti:
lebih cepat dari:
Hal lain adalah jika Anda perlu membuat banyak contoh. Kemudian sakelar yang hanya memanggil sekali getType () lebih cepat.
sumber
Jika kecepatan adalah satu-satunya tujuan Anda maka menggunakan konstanta int untuk mengidentifikasi sub-kelas tampaknya mencukur milidetik waktu itu
desain OO yang mengerikan, tetapi jika analisis kinerja Anda menunjukkan ini adalah tempat Anda mengalami kemacetan maka mungkin. Dalam kode saya, kode pengiriman membutuhkan 10% dari total waktu eksekusi dan ini mungkin berkontribusi pada peningkatan kecepatan total 1%.
sumber
Anda harus mengukur / profil jika itu benar-benar masalah kinerja dalam proyek Anda. Jika itu saya sarankan desain ulang - jika mungkin. Saya cukup yakin Anda tidak bisa mengalahkan implementasi asli platform (ditulis dalam C). Anda juga harus mempertimbangkan pewarisan berganda dalam kasus ini.
Anda harus memberi tahu lebih banyak tentang masalahnya, mungkin Anda bisa menggunakan toko asosiatif, misalnya Peta <Kelas, Objek> jika Anda hanya tertarik pada jenis beton.
sumber
Berkenaan dengan catatan Peter Lawrey bahwa Anda tidak perlu instanceof untuk kelas akhir dan hanya dapat menggunakan referensi kesetaraan, hati-hati! Meskipun kelas akhir tidak dapat diperpanjang, mereka tidak dijamin dimuat oleh classloader yang sama. Hanya gunakan x.getClass () == SomeFinal.class atau sejenisnya jika Anda benar-benar positif bahwa hanya ada satu classloader yang dimainkan untuk bagian kode itu.
sumber
Saya juga lebih suka pendekatan enum, tapi saya akan menggunakan kelas dasar abstrak untuk memaksa subclass untuk mengimplementasikan
getType()
metode ini.sumber
Saya pikir mungkin layak mengirimkan contoh tandingan ke konsensus umum di halaman ini bahwa "instanceof" tidak cukup mahal untuk dikhawatirkan. Saya menemukan saya memiliki beberapa kode dalam loop batin yang (dalam beberapa upaya bersejarah optimasi) lakukan
di mana memanggil head () pada SingleItem mengembalikan nilai tidak berubah. Mengganti kode dengan
memberi saya kecepatan dari 269ms ke 169ms, terlepas dari kenyataan bahwa ada beberapa hal yang cukup berat terjadi dalam loop, seperti konversi string-to-double. Tentu saja mungkin bahwa percepatan lebih disebabkan oleh penghapusan cabang bersyarat daripada menghilangkan instance dari operator itu sendiri; tapi saya pikir itu layak disebut.
sumber
if
itu sendiri. Jika distribusitrue
s danfalse
s mendekati genap, eksekusi spekulatif menjadi tidak berguna, yang mengarah pada kelambatan yang signifikan.Anda fokus pada hal yang salah. Perbedaan antara instanceof dan metode lain untuk memeriksa hal yang sama mungkin bahkan tidak dapat diukur. Jika kinerja sangat penting maka Java mungkin bahasa yang salah. Alasan utama adalah bahwa Anda tidak dapat mengontrol ketika VM memutuskan ingin mengumpulkan sampah, yang dapat mengambil CPU hingga 100% selama beberapa detik dalam program besar (MagicDraw 10 sangat bagus untuk itu). Kecuali Anda mengendalikan setiap komputer, program ini akan berjalan pada Anda tidak dapat menjamin versi JVM mana yang akan dihidupkan, dan banyak dari yang lebih tua memiliki masalah kecepatan utama. Jika itu adalah aplikasi kecil Anda mungkin tidak masalah dengan Java, tetapi jika Anda terus membaca dan membuang data, maka Anda akan melihat kapan GC masuk.
sumber