Ось ще одна техніка, яку я натрапив на днях:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
Collections.nCopies
Виклик створює List
містять n
копії будь-яке значення , ви надаєте. У цьому випадку це Integer
значення в коробці 1. Звичайно, він фактично не створює список з n
елементами; він створює "віртуалізований" список, що містить лише значення та довжину, і будь-який виклик в get
межах діапазону просто повертає значення. nCopies
Метод був навколо з Колекцією рамкової був введений ще в JDK 1.2. Звичайно, можливість створення потоку з його результату була додана в Java SE 8.
Велика справа, ще один спосіб зробити те ж саме приблизно в однаковій кількості рядків.
Однак ця методика швидша за IntStream.generate
та IntStream.iterate
наближається, і на диво, вона також швидша за IntStream.range
підхід.
Бо iterate
і generate
результат, мабуть, не надто дивний. Рамка потоків (насправді сплітератори для цих потоків) побудована на припущенні, що лямбди потенційно можуть генерувати різні значення кожного разу, і що вони будуть генерувати необмежену кількість результатів. Це робить паралельне розщеплення особливо складним. iterate
Метод також проблематичний для цього випадку , тому що кожен виклик вимагає результату попередньої. Таким чином, потоки, що використовують generate
і iterate
не дуже добре для отримання повторних констант.
Відносно погані показники роботи range
дивують. Це теж віртуалізується, тому елементи насправді не всі існують у пам'яті, а розмір відомий наперед. Це повинно створити швидкий і легко паралельний сплітератор. Але це дивно не дуже добре. Можливо, причина полягає в тому, що range
необхідно обчислити значення для кожного елемента діапазону, а потім викликати функцію на ньому. Але ця функція просто ігнорує вхід і повертає константу, тому я здивований, що це не вбудовано і не вбито.
Collections.nCopies
Техніка повинна зробити бокс / розпакування для того , щоб обробляти значення, так як немає примітивних спеціалізацій List
. Оскільки значення є однаковим щоразу, воно в основному є одним кодом, і це поле ділиться всіма n
копіями. Я підозрюю, що бокс / розблокування дуже оптимізований, навіть непростий, і це може бути добре накреслено.
Ось код:
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();
}
Ось результати JMH: (2,8 ГГц 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
У версії ncopies є досить велика дисперсія, але в цілому вона здається комфортно на 20 разів швидшою, ніж версія діапазону. (Я б дуже хотів вірити, що я щось не так зробив.)
Я здивований тим, наскільки добре nCopies
працює техніка. Всередині він не особливо особливий, при цьому потік віртуалізованого списку просто реалізується за допомогою IntStream.range
! Я очікував, що потрібно буде створити спеціалізований розширювач, щоб це швидко пройшло, але це вже здається досить непоганим.