Ответ 1
В общем, вы не представляете, сколько работы сделано для пользователя, прошедшего через tryAdvance
или forEachRemaining
. Ни поток, ни FJP не знают об этом, так как это зависит от кода, предоставленного пользователем. Это может быть намного быстрее или намного медленнее, чем процедура расщепления. Например, вы можете вводить два элемента, но обработка каждого элемента занимает один час, поэтому разделение этого ввода очень разумно.
Я обычно разбиваю вход как можно больше. Существует три трюка, которые можно использовать для улучшения расщепления:
-
Если трудно разделить поровну, но вы можете отслеживать (или, по крайней мере, грубо оценить) размер каждой части, не стесняйтесь делиться неравномерно. Реализация потока будет делать дальнейшее разделение для большей части. Не забывайте о характеристиках
SIZED
иSUBSIZED
. -
Переместите твердую часть разделения на следующий вызов
tryAdvance
/forEachRemaining
. Например, предположим, что у вас есть известное количество перестановок, а вtrySplit
вы переходите к другой перестановке. Что-то вроде этого:public class MySpliterator implements Spliterator<String> { private long position; private String currentPermutation; private final long limit; MySpliterator(long position, long limit, String currentPermutation) { this.position = position; this.limit = limit; this.currentPermutation = currentPermutation; } @Override public Spliterator<String> trySplit() { if(limit - position <= 1) return null; long newPosition = (position+limit)>>>1; Spliterator<String> prefix = new MySpliterator(position, newPosition, currentPermutation); this.position = newPosition; this.currentPermutation = calculatePermutation(newPosition); // hard part return prefix; } ... }
Переместите твердую часть на следующий вызов
tryAdvance
следующим образом:@Override public Spliterator<String> trySplit() { if(limit - position <= 1) return null; long newPosition = (position+limit)>>>1; Spliterator<String> prefix = new MySpliterator(position, newPosition, currentPermutation); this.position = newPosition; this.currentPermutation = null; return prefix; } @Override public boolean tryAdvance(Consumer<? super String> action) { if(currentPermutation == null) currentPermutation = calculatePermutation(position); // hard part ... }
Таким образом, жесткая часть также будет выполняться параллельно с обработкой префикса.
-
Если у вас осталось не так много элементов в текущем разделителе (например, менее 10), и запрос на рассылку был запрошен, вероятно, это хорошо, только чтобы перейти к половине ваших элементов, собирая их в массив, а затем создать разделитель на основе массива для этого префикса (аналогично тому, как это делается в
AbstractSpliterator.trySplit()
). Здесь вы управляете всем кодом, так что вы можете заранее измерить, насколько нормальнаяtrySplit
медленнее, чемtryAdvance
, и оценить порог, когда вы должны переключиться на разделение на основе массива.