Performance Comparison of Primitive Lists in Java – Java中原始列表的性能比较

最后修改: 2019年 2月 20日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’re going to compare the performance of some popular primitive list libraries in Java.

在本教程中,我们将比较一些流行的Java中的primitive list库的性能

For that, we’ll test the add(), get(), and contains() methods for each library.

为此,我们将测试每个库的add()、get()、 contains()方法。

2. Performance Comparison

2.性能比较

Now, let’s find out which library offers a fast working primitive collections API.

现在,让我们看看哪个库提供了快速工作的原始集合API

For that, let’s compare the List analogs from Trove, Fastutil, and Colt. We’ll use the JMH (Java Microbenchmark Harness) tool to write our performance tests.

为此,让我们比较一下List的模拟,Trove、FastutilColt。我们将使用JMH(Java Microbenchmark Harness)工具来编写我们的性能测试。

2.1. JMH Parameters

2.1.JMH的参数

We’ll run our benchmark tests with the following parameters:

我们将用以下参数来运行我们的基准测试。

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

Here, we want to measure the execution time for each benchmark method. Also, we want to display our results in milliseconds.

在这里,我们想测量每个基准方法的执行时间。同时,我们想以毫秒为单位显示我们的结果。

The @State annotation indicates that the variables declared in the class won’t be the part of running benchmark tests. However, we can then use them in our benchmark methods.

@State注解表明,在类中声明的变量不会成为运行基准测试的一部分。然而,我们可以在我们的基准测试方法中使用它们。

Additionally, let’s define and initialize our lists of primitives:

此外,让我们定义并初始化我们的基元列表。

public static class PrimitivesListPerformance {
    private List<Integer> 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;
}

Now, we’re ready to write our benchmarks.

现在,我们准备编写我们的基准。

3. add()

3.add()

First, let’s test adding the elements into our primitive lists. We’ll also add one for ArrayList as our control.

首先,让我们测试一下将元素添加到我们的原始列表中。我们还将为ArrayList添加一个作为我们的控件。

3.1. Benchmark Tests

3.1.基准测试

The first micro-benchmark is for the ArrayLists add() method:

第一个微观基准是针对ArrayLists add()方法。

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

Similarly, for the Trove’s TIntArrayList.add():

同样,对于Trove的TIntArrayList.add()

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

Likewise, Colt’s IntArrayList.add() looks like:

同样,Colt的IntArrayList.add() 看起来像。

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

And, for Fastutil library, the IntArrayList.add() method benchmark will be:

而且,对于Fastutil库,IntArrayList.add()方法的基准将是。

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

3.2. Test Results

3.2.测试结果

Now, we run and compare results:

现在,我们运行并比较结果。

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

From the results, we can clearly see that ArrayList’s add() is the slowest option.

从结果中,我们可以清楚地看到,ArrayList的add()是最慢的选项。

This is logical, as we explained in the primitive list libraries article, ArrayList will use boxing/autoboxing to store the int values inside the collection. Therefore, we have significant slowdown here.

这是合乎逻辑的,正如我们在primitive list libraries一文中解释的那样,ArrayList将使用装箱/自动装箱来存储集合内的int值。因此,我们在这里有明显的减速。

On the other hand, the add() methods for Colt and Fastutil were the fastest.

另一方面,Colt和Fastutil的add()方法是最快的。

Under the hood, all three libraries store the values inside of an int[]. So why do we have different running times for their add() methods?

在引擎盖下,这三个库都将值存储在int[]中。那么,为什么他们的add()方法的运行时间不同呢?

The answer is how they grow the int[] when the default capacity is full:

答案是当默认容量已满时,他们如何增长int[]

  • Colt will grow its internal int[] only when it becomes full
  • In contrast, Trove and Fastutil will use some additional calculations while expanding the int[] container

That’s why Colt is winning in our test results.

这就是为什么柯尔特公司在我们的测试结果中获胜。

4. get()

4.get()

Now, let’s add the get() operation micro-benchmark.

现在,让我们添加get()操作的微型基准。

4.1. Benchmark Tests

4.1.基准测试

First, for the ArrayList’s get() operation:

首先,对于ArrayList’sget() 操作。

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

Similarly, for the Trove’s TIntArrayList we’ll have:

同样地,对于Trove的TIntArrayList,我们将有。

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

And, for Colt’s cern.colt.list.IntArrayList, the get() method will be:

而且,对于Colt的cern.colt.list.IntArrayList,get()方法将是。

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

Finally, for the Fastutil’s IntArrayList we’ll test the getInt() operation:

最后,对于Fastutil的IntArrayList,我们将测试getInt()操作。

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

4.2. Test Results

4.2.测试结果

After, we run the benchmarks and see the results:

之后,我们运行基准测试,看看结果。

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

Although the score difference isn’t much, we can notice that getArrayList() works slower.

虽然分数差别不大,但我们可以注意到,getArrayList()的工作速度较慢。

For the rest of the libraries, we have almost identical get() method implementations. They will retrieve the value immediately from the int[] without any further work. That’s why Colt, Fastutil, and Trove have similar performances for the get() operation.

对于其余的库,我们有几乎相同的get()方法实现。它们将立即从int[]中获取值,而不需要任何进一步的工作。这就是为什么Colt、Fastutil和Trove在get()操作上有相似的表现。

5. contains()

5.contains()

Finally, let’s test the contains() method for each type of the list.

最后,让我们为列表的每种类型测试一下contains()方法。

5.1. Benchmark Tests

5.1.基准测试

Let’s add the first micro-benchmark for ArrayList’s contains() method:

让我们为ArrayLists contains() 方法添加第一个微观基准。

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

Similarly, for the Trove’s TIntArrayList the contains() benchmark will be:

同样,对于Trove的TIntArrayList来说,contains()的基准将是。

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

Likewise, the test for Colt’s cern.colt.list.IntArrayList.contains() is:

同样,Colt的cern.colt.list.IntArrayList.contains()的测试也是如此。

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

And, for Fastutil’s IntArrayList, the contains() method test looks like:

而且,对于Fastutil的IntArrayList, contains()方法测试看起来像。

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

5.2. Test Results

5.2.测试结果

Finally, we run our tests and compare the results:

最后,我们运行我们的测试并比较结果。

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

As usual, the containsArrayList method has the worst performance. In contrast, Trove, Colt, and Fastutil have better performance compared to Java’s core solution.

像往常一样,containsArrayList方法的性能最差。相比之下,Trove、Colt和Fastutil与Java的核心解决方案相比具有更好的性能。

This time, it’s up to the developer which library to choose. The results for all three libraries are close enough to consider them identical.

这一次,就看开发者选择哪个库了。所有三个库的结果都很接近,可以认为它们是相同的。

6. Conclusion

6.结论

In this article, we investigated the actual runtime performance of primitive lists through the JVM benchmark tests. Moreover, we compared the test results with the JDK’s ArrayList.

在这篇文章中,我们通过JVM基准测试调查了原始列表的实际运行时性能。此外,我们将测试结果与JDK的ArrayList进行了比较。

Also, keep in mind that the numbers we present here are just JMH benchmark results – always test in the scope of a given system and runtime.

此外,请记住,我们在这里提出的数字只是JMH的基准结果 – 始终在特定的系统和运行时间范围内进行测试。

As usual, the complete code for this article is available over on GitHub.

像往常一样,本文的完整代码可以在GitHub上找到