Рубрики
Без рубрики

Сравнение производительности списков примитивов в Java

Сравните производительность некоторых популярных библиотек примитивных списков в Java

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы собираемся сравнить производительность некоторых популярных библиотек примитивных списков в Java .

Для этого мы протестируем методы add(), get(), и contains() для каждой библиотеки.

2. Сравнение производительности

Теперь, давайте выясним, какая библиотека предлагает быстро работающий API примитивных коллекций .

Для этого давайте сравним List аналоги из Trove, Fastutil и Colt . Мы будем использовать инструмент JMH (Java Microbenchmark Harness) для написания наших тестов производительности.

2.1. Параметры JMH

Мы проведем наши тестовые тесты со следующими параметрами:

@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Measurement(batchSize = 100000, iterations = 10)
@Warmup(batchSize = 100000, iterations = 10)
@State(Scope.Thread)
public class PrimitivesListPerformance {
}

Здесь мы хотим измерить время выполнения для каждого метода бенчмарка. Кроме того, мы хотим отображать наши результаты в миллисекундах.

Аннотация @State указывает на то, что переменные, объявленные в классе, не будут частью выполнения контрольных тестов. Однако затем мы можем использовать их в наших тестовых методах.

Кроме того, давайте определим и инициализируем наши списки примитивов:

public static class PrimitivesListPerformance {
    private List arrayList = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
    private TIntArrayList tList = new TIntArrayList(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
    private cern.colt.list.IntArrayList coltList = new cern.colt.list.IntArrayList(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
    private IntArrayList fastUtilList = new IntArrayList(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});

    private int getValue = 4;
}

Теперь мы готовы написать наши контрольные показатели.

3. добавьте()

Во-первых, давайте проверим добавление элементов в наши примитивные списки. Мы также добавим один для ArrayList в качестве нашего элемента управления.

3.1. Контрольные тесты

Первый микро-бенчмарк предназначен для метода ArrayList ‘ |/s add() :

@Benchmark
public boolean addArrayList() {
    return arrayList.add(getValue);
}

Аналогично, для клада TIntArrayList.add() :

@Benchmark
public boolean addTroveIntList() {
    return tList.add(getValue);
}

Аналогично, Colt IntArrayList.add() выглядит следующим образом:

@Benchmark
public void addColtIntList() {
    coltList.add(getValue);
}

А для библиотеки Fastutil эталоном метода IntArrayList.add() будет:

@Benchmark
public boolean addFastUtilIntList() {
    return fastUtilList.add(getValue);
}

А для библиотеки Fastutil эталоном метода || IntArrayList.add() || будет:

Теперь мы запускаем и сравниваем результаты:

Benchmark           Mode  Cnt  Score   Error  Units
addArrayList          ss   10  4.527 ± 4.866  ms/op
addColtIntList        ss   10  1.823 ± 4.360  ms/op
addFastUtilIntList    ss   10  2.097 ± 2.329  ms/op
addTroveIntList       ss   10  3.069 ± 4.026  ms/op

Из результатов мы ясно видим, что Arraylist add() является самым медленным вариантом.

Это логично, как мы объяснили в статье библиотеки примитивных списков |, ArrayList будет использовать бокс/автобокс для хранения значений int внутри коллекции. Поэтому у нас здесь наблюдается значительное замедление темпов роста.

С другой стороны, методы add() для Colt и Fastutil были самыми быстрыми.

Под капотом все три библиотеки хранят значения внутри int[] . Так почему же у нас разное время выполнения для их методов add () ?

Ответ заключается в том, как они увеличивают int [] , когда емкость по умолчанию заполнена:

  • Кольт вырастит свой внутренний int[] только когда он станет полным
  • В отличие от этого, Trove и Fastutil будут использовать некоторые дополнительные вычисления при расширении контейнера int[]

Вот почему Кольт выигрывает в результатах наших тестов.

4. получить()

Теперь давайте добавим микро-бенчмарк get() operation.

4.1. Контрольные тесты

Во – первых, для операции ArrayList’ s get() :

@Benchmark
public int getArrayList() {
    return arrayList.get(getValue);
}

Аналогично, для клада ТИнтАррайЛист у нас будет:

@Benchmark
public int getTroveIntList() {
    return tList.get(getValue);
}

И, для Кольта cern.colt.list.IntArrayList, метод get() будет:

@Benchmark
public int getColtIntList() {
    return coltList.get(getValue);
}

Наконец, для Fastutil’s IntArrayList мы протестируем операцию getInt() :

@Benchmark
public int getFastUtilIntList() {
    return fastUtilList.getInt(getValue);
}

4.2. Результаты испытаний

После этого мы запускаем тесты и видим результаты:

Benchmark           Mode  Cnt  Score   Error  Units
getArrayList        ss     20  5.539 ± 0.552  ms/op
getColtIntList      ss     20  4.598 ± 0.825  ms/op
getFastUtilIntList  ss     20  4.585 ± 0.489  ms/op
getTroveIntList     ss     20  4.715 ± 0.751  ms/op

Хотя разница в баллах невелика, мы можем заметить, что getArrayList() работает медленнее.

Для остальных библиотек у нас есть почти идентичные реализации метода get () . Они немедленно извлекут значение из int[] без какой-либо дальнейшей работы. Вот почему Colt, Fastutil и Trove имеют схожие характеристики для операции get () .

5. содержит()

Наконец, давайте протестируем метод contains() для каждого типа списка.

5.1. Контрольные тесты

Давайте добавим первый микро-бенчмарк для метода ArrayList’ s contains() :

@Benchmark
public boolean containsArrayList() {
    return arrayList.contains(getValue);
}

Аналогично, для клада TIntArrayList |/содержит() бенчмарк будет:

@Benchmark
public boolean containsTroveIntList() {
    return tList.contains(getValue);
}

Аналогично, тест для Кольта cern.colt.list.IntArrayList.contains() is:

@Benchmark
public boolean containsColtIntList() {
    return coltList.contains(getValue);
}

И, для Fastutil’s IntArrayList, тест метода contains() выглядит следующим образом:

@Benchmark
public boolean containsFastUtilIntList() {
    return fastUtilList.contains(getValue);
}

5.2. Результаты испытаний

Наконец, мы запускаем наши тесты и сравниваем результаты:

Benchmark                  Mode  Cnt   Score    Error  Units
containsArrayList          ss     20   2.083  ± 1.585  ms/op
containsColtIntList        ss     20   1.623  ± 0.960  ms/op
containsFastUtilIntList    ss     20   1.406  ± 0.400  ms/op
containsTroveIntList       ss     20   1.512  ± 0.307  ms/op

Как обычно, метод contains ArrayList имеет худшую производительность . Напротив, Trove, Colt и Fastutil имеют лучшую производительность по сравнению с основным решением Java.

На этот раз выбор библиотеки зависит от разработчика. Результаты для всех трех библиотек достаточно близки, чтобы считать их идентичными.

6. Заключение

В этой статье мы исследовали фактическую производительность примитивных списков во время выполнения с помощью тестов JVM benchmark. Кроме того, мы сравнили результаты тестирования с ArrayList JDK .

Кроме того, имейте в виду, что цифры, которые мы представляем здесь, являются просто результатами теста JMH – всегда тестируйте в рамках данной системы и среды выполнения.

Как обычно, полный код этой статьи доступен на GitHub .