Быстро снижается пропускная способность потока с цепочечными операциями? - PullRequest
0 голосов
/ 04 октября 2018

Я ожидал, что у простых промежуточных потоковых операций, таких как limit(), очень мало накладных расходов.Но разница в пропускной способности между этими примерами на самом деле значительна:

final long MAX = 5_000_000_000L;

LongStream.rangeClosed(0, MAX)
          .count();
// throughput: 1.7 bn values/second


LongStream.rangeClosed(0, MAX)
          .limit(MAX)
          .count();
// throughput: 780m values/second

LongStream.rangeClosed(0, MAX)
          .limit(MAX)
          .limit(MAX)
          .count();
// throughput: 130m values/second

LongStream.rangeClosed(0, MAX)
          .limit(MAX)
          .limit(MAX)
          .limit(MAX)
          .count();
// throughput: 65m values/second

Мне любопытно: в чем причина быстрого снижения пропускной способности?Это согласованный шаблон с цепочечными операциями потока или моими настройками теста?(Я пока не использовал JMH, просто настроил быстрый эксперимент с секундомером)

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

Это незавершенная реализация в Stream API (не знаю, как его вызвать иначе).

В первом примере вы знаете count, фактически не считая- нет filter (например) операций, которые могли бы очистить внутренний флаг с именем SIZED.На самом деле немного интересно, если вы измените это и просмотрите:

System.out.println(
            LongStream.rangeClosed(0, Long.MAX_VALUE)
                    .spliterator()
                    .hasCharacteristics(Spliterator.SIZED)); // reports false

System.out.println(
            LongStream.rangeClosed(0, Long.MAX_VALUE - 1) // -1 here
                    .spliterator()
                    .hasCharacteristics(Spliterator.SIZED)); // reports true

и limit - даже если нет фундаментальных (AFAIK) ограничений, не вводится флаг SIZED:

System.out.println(LongStream.rangeClosed(0, MAX)
            .limit(MAX)
            .spliterator()
            .hasCharacteristics(Spliterator.SIZED)); // reports false

Поскольку вы учитываете всюду, тот факт, что внутренне API потоков не знает, является ли поток SIZED, просто считается;в то время как если поток равен SIZED - количество сообщений будет хорошим, мгновенным.

Когда вы добавляете limit несколько раз, вы только усугубляете ситуацию, поскольку она должна ограничивать эти пределы каждый раз.

В java-9 дела улучшились, например, для случая:

System.out.println(LongStream.rangeClosed(0, MAX)
            .map(x -> {
                System.out.println(x);
                return x;
            })
            .count());

В этом случае map вообще не вычисляется, так как нет необходимости в -никакая промежуточная операция не изменяет размер потока.

Теоретически потоковый API может видеть, что вы limit ing и 1) вводите флаг SIZED 2) видите, что у вас есть несколько вызовов limitи просто, вероятно, возьмите последний. На данный момент этого не сделано, но это имеет очень ограниченную область, сколько людей будут злоупотреблять limit таким образом?Поэтому не ожидайте каких-либо улучшений в этой части в ближайшее время.

0 голосов
/ 04 октября 2018

limit приведет к созданию среза из потока с итератором разбиения (для параллельной работы).Одним словом: неэффективно.Большие накладные расходы для неоперативного здесь.И то, что два последовательных вызова limit приводят к двум слайсам, это позор.

Вы должны взглянуть на реализацию IntStream.limit.

Поскольку потоки все еще относительно новые, оптимизация должнаприйти последним;когда производственный код существует.Выполнение лимита 3 раза кажется надуманным.

...