Terkadang Anda ingin memfilter Stream
dengan lebih dari satu kondisi:
myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...
atau Anda dapat melakukan hal yang sama dengan kondisi kompleks dan satu filter
:
myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...
Dugaan saya adalah bahwa pendekatan kedua memiliki karakteristik kinerja yang lebih baik, tetapi saya tidak mengetahuinya .
Pendekatan pertama menang dalam keterbacaan, tetapi apa yang lebih baik untuk kinerja?
Jawaban:
Kode yang harus dieksekusi untuk kedua alternatif ini sangat mirip sehingga Anda tidak dapat memprediksi hasilnya dengan andal. Struktur objek yang mendasarinya mungkin berbeda tetapi itu bukan tantangan bagi pengoptimal hotspot. Jadi itu tergantung pada kondisi lain di sekitarnya yang akan menghasilkan eksekusi yang lebih cepat, jika ada perbedaan.
Menggabungkan dua instance filter menciptakan lebih banyak objek dan karenanya lebih banyak mendelegasikan kode tetapi ini dapat berubah jika Anda menggunakan referensi metode daripada ekspresi lambda, misalnya ganti
filter(x -> x.isCool())
denganfilter(ItemType::isCool)
. Dengan begitu Anda telah menghilangkan metode pendelegasian sintetis yang dibuat untuk ekspresi lambda Anda. Jadi, menggabungkan dua filter menggunakan dua metode referensi dapat membuat kode delegasi yang sama atau lebih kecil dari satufilter
permintaan tunggal menggunakan ekspresi lambda&&
.Tetapi, seperti dikatakan, overhead semacam ini akan dihilangkan oleh pengoptimal HotSpot dan dapat diabaikan.
Secara teori, dua filter bisa lebih mudah diparalelkan daripada satu filter tetapi itu hanya relevan untuk tugas-tugas yang agak komputasional¹.
Jadi tidak ada jawaban sederhana.
Intinya adalah, jangan berpikir tentang perbedaan kinerja seperti di bawah ambang batas deteksi bau. Gunakan apa yang lebih mudah dibaca.
¹ ... dan akan membutuhkan implementasi yang melakukan pemrosesan paralel dari tahapan selanjutnya, jalan yang saat ini tidak diambil oleh implementasi Stream standar
sumber
Kondisi filter yang kompleks lebih baik dalam perspektif kinerja, tetapi kinerja terbaik akan menunjukkan mode lama untuk loop dengan standar
if clause
adalah opsi terbaik. Perbedaan pada array 10 elemen perbedaan kecil mungkin ~ 2 kali, untuk array besar perbedaannya tidak begitu besar.Anda dapat melihat pada proyek GitHub saya , di mana saya melakukan tes kinerja untuk opsi iterasi berbagai array
Untuk ops / throughput elemen array 10 kecil: Untuk ops / s throughput elemen 10.000 sedang : Untuk ops / array throughput elemen yang besar 1.000.000:
CATATAN: tes berjalan
UPDATE: Java 11 memiliki beberapa kemajuan pada kinerja, tetapi dinamika tetap sama
Mode benchmark: Throughput, ops / waktu
sumber
Tes ini menunjukkan bahwa opsi kedua Anda dapat bekerja lebih baik secara signifikan. Temuan pertama, lalu kodenya:
sekarang kodenya:
sumber
Test #1: {count=100, sum=7207, min=65, average=72.070000, max=91} Test #3: {count=100, sum=7959, min=72, average=79.590000, max=97} Test #2: {count=100, sum=8869, min=79, average=88.690000, max=110}
Ini adalah hasil dari 6 kombinasi berbeda dari uji sampel yang dibagikan oleh @Hank D. Jelas bahwa predikat formulir
u -> exp1 && exp2
sangat berkinerja dalam semua kasus.sumber