Различия в производительности между списком объектов против потребителей

1

У меня есть этот метод, когда какая-то точка создается и добавляется в новый список. Это имеет недостаток в том, что каждый раз создается слишком много объектов.

Теперь я подумал о возвращении потребителя точечного потребителя, что-то вроде:

PointConsumer { void apply(int x, int y); }
PointIterator { void apply(PointConsumer pc); }

Каждый раз, когда я генерирую точку, я бы объединил новую лямбду со старой:

pointIterator = pc -> { pointIterator.apply(pc); pc.apply(x, y); }

Будет ли этот подход медленнее или больше потребителя памяти, чем предыдущий? Оставляя в стороне, если различия больше или меньше.

  • 0
    Вы имели в виду, что имена методов должны быть accept или apply ?
  • 0
    Что происходит с этими объектами впоследствии? В любом случае, если вам нужно сохранить их в памяти, проще добавить их в список напрямую, а не через потребителя.
Показать ещё 2 комментария
Теги:
list
lambda
iteration

1 ответ

5
Лучший ответ

Вот вам пример JMH того, что я понял из вашего довольно общего вопроса. Надеюсь, это поможет вам сделать некоторые выводы об эффективности, о которой вы просили.

@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(Measure.SIZE)
@Warmup(iterations = 5, time = 100, timeUnit=MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit=MILLISECONDS)
@State(Scope.Thread)
@Threads(1)
@Fork(1)
public class Measure
{
  public static final int SIZE = 1;
  private static final int LIMIT = 50;

  interface PointConsumer { void apply(int x, int y); }
  interface PointIterator { void apply(PointConsumer pc); }

  PointIterator pointIterator;
  List<Point> points;

  @Setup public void setup() {
    pointIterator = pc -> {};
    range(0,LIMIT).forEach(x -> range(0,LIMIT).forEach(y -> {
      final PointIterator pi = pointIterator;
      pointIterator = pc -> { pi.apply(pc); pc.apply(x, y); };
    }));
    points = range(0,LIMIT).mapToObj(i->i).flatMap(
        x -> range(0,LIMIT).mapToObj(y -> new Point(x,y)))
        .collect(toList());
  }

  @Benchmark public int pointIterator() {
    final int sum[] = {0};
    pointIterator.apply((x,y) -> sum[0] += x + y );
    return sum[0];
  }

  @Benchmark public int list() {
    int sum = 0;
    for (Point p : points) sum += p.x + p.y;
    return sum;
  }
}

Результаты измерения:

Benchmark                    Mode  Samples   Score   Error  Units
o.s.Measure.list             avgt        5   3,921 ± 0,408  us/op
o.s.Measure.pointIterator    avgt        5  17,970 ± 1,740  us/op

Мои выводы:

  1. очевидно, это медленнее, чем простой подход ArrayList;
  2. создается такое же количество объектов (лексическая среда, закрытая лямбдой, удерживается в объекте);
  3. ваш дизайн приводит к рекурсивной оценке, взорвав стек даже для скромных размеров списка.
  • 0
    Хорошо, спасибо, что нашли время для тестирования.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню