Inilah teknik lain yang saya lakukan beberapa hari yang lalu:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
The Collections.nCopiespanggilan menciptakan Listmengandung nsalinan dari nilai apa pun yang Anda berikan. Dalam hal ini nilai kotaknya Integer1. Tentu saja itu tidak benar-benar membuat daftar dengan nelemen; itu membuat daftar "tervirtualisasi" yang hanya berisi nilai dan panjang, dan setiap panggilan ke getdalam jangkauan hanya mengembalikan nilai. The nCopiesMetode telah ada sejak Koleksi Kerangka diperkenalkan kembali dengan cara di JDK 1.2. Tentu saja, kemampuan untuk membuat aliran dari hasilnya ditambahkan di Java SE 8.
Masalah besar, cara lain untuk melakukan hal yang sama di sekitar jumlah baris yang sama.
Namun, teknik ini lebih cepat dari IntStream.generatedan IntStream.iteratependekatan, dan mengejutkan, itu juga lebih cepat dari IntStream.rangependekatan.
Untuk iterate dan generatehasilnya mungkin tidak terlalu mengejutkan. Kerangka aliran (sebenarnya, Pemisah untuk aliran ini) dibangun dengan asumsi bahwa lambda akan berpotensi menghasilkan nilai yang berbeda setiap saat, dan bahwa mereka akan menghasilkan jumlah hasil yang tidak terbatas. Hal ini membuat pemisahan paralel menjadi sulit. The iterateMetode juga bermasalah untuk kasus ini karena setiap panggilan membutuhkan hasil dari yang sebelumnya. Jadi aliran menggunakan generatedan iteratetidak melakukannya dengan baik untuk menghasilkan konstanta berulang.
Kinerja yang relatif buruk rangecukup mengejutkan. Ini juga tervirtualisasi, jadi elemen sebenarnya tidak semuanya ada di memori, dan ukurannya diketahui sebelumnya. Ini akan membuat spliterator yang cepat dan mudah diparalelkan. Tapi secara mengejutkan tidak berhasil dengan baik. Mungkin alasannya adalah karena rangeharus menghitung nilai untuk setiap elemen rentang dan kemudian memanggil fungsi di atasnya. Tapi fungsi ini mengabaikan inputnya dan mengembalikan konstanta, jadi saya terkejut ini tidak sebaris dan mati.
The Collections.nCopiesTeknik hubungannya tinju / pembukaan kemasan untuk menangani nilai-nilai, karena tidak ada spesialisasi primitif List. Karena nilainya adalah sama setiap kali, pada dasarnya dikotak sekali dan kotak itu dibagikan oleh semua nsalinan. Saya menduga tinju / unboxing sangat dioptimalkan, bahkan diintrinsifikasi, dan dapat disisipkan dengan baik.
Berikut kodenya:
public static final int LIMIT = 500_000_000;
public static final long VALUE = 3L;
public long range() {
return
LongStream.range(0, LIMIT)
.parallel()
.map(i -> VALUE)
.map(i -> i % 73 % 13)
.sum();
}
public long ncopies() {
return
Collections.nCopies(LIMIT, VALUE)
.parallelStream()
.mapToLong(i -> i)
.map(i -> i % 73 % 13)
.sum();
}
Dan berikut adalah hasil JMH: (2.8GHz Core2Duo)
Benchmark Mode Samples Mean Mean error Units
c.s.q.SO18532488.ncopies thrpt 5 7.547 2.904 ops/s
c.s.q.SO18532488.range thrpt 5 0.317 0.064 ops/s
Ada cukup banyak variasi dalam versi ncopies, tetapi secara keseluruhan tampaknya nyaman 20x lebih cepat daripada versi range. (Namun, saya akan sangat bersedia untuk percaya bahwa saya telah melakukan sesuatu yang salah.)
Saya terkejut melihat seberapa baik nCopiesteknik ini bekerja. Secara internal itu tidak terlalu istimewa, dengan aliran daftar virtual yang hanya diimplementasikan menggunakan IntStream.range! Saya berharap akan perlu membuat spliterator khusus agar ini bekerja dengan cepat, tetapi tampaknya sudah cukup bagus.