Javaでのプリミティブリストのパフォーマンス比較
1. 概要
このチュートリアルでは、Javaで人気のあるいくつかのプリミティブリストライブラリのパフォーマンスを比較します。
そのために、各ライブラリの add()、get()、、および contains()メソッドをテストします。
2. パフォーマンスの比較
それでは、どのライブラリが高速で動作するプリミティブコレクションAPIを提供しているかを調べてみましょう。
そのために、 Trove、Fastutil 、およびColtのListアナログを比較してみましょう。 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<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;
}
これで、ベンチマークを作成する準備が整いました。
3. add()
まず、プリミティブリストに要素を追加してテストしましょう。 また、コントロールとしてArrayList用に1つ追加します。
3.1. ベンチマークテスト
最初のマイクロベンチマークは、 ArrayList’ s add()メソッド用です。
@Benchmark
public boolean addArrayList() {
return arrayList.add(getValue);
}
同様に、Troveの 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);
}
3.2. 試験結果
次に、実行して結果を比較します。
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値を格納します。 したがって、ここでは大幅な速度低下が発生します。
一方、ColtとFastutilの add()メソッドが最速でした。
内部的には、3つのライブラリすべてが int[]内に値を格納します。 では、なぜ add()メソッドの実行時間が異なるのでしょうか。
答えは、デフォルトの容量がいっぱいになったときに int[]をどのように拡張するかです。
- Coltは、いっぱいになったときにのみ内部 int[]を成長させます
- 対照的に、 TroveとFastutilは、 int [] コンテナを拡張するときに、いくつかの追加の計算を使用します
そのため、Coltがテスト結果で勝っています。
4. get()
それでは、 get()操作のマイクロベンチマークを追加しましょう。
4.1. ベンチマークテスト
まず、 ArrayList’ s get()操作の場合:
@Benchmark
public int getArrayList() {
return arrayList.get(getValue);
}
同様に、Troveの TIntArrayList には、次のものがあります。
@Benchmark
public int getTroveIntList() {
return tList.get(getValue);
}
また、Coltの cern.colt.list.IntArrayListの場合、 get()メソッドは次のようになります。
@Benchmark
public int getColtIntList() {
return coltList.get(getValue);
}
最後に、Fastutilの 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()
最後に、リストのタイプごとに contains()メソッドをテストしてみましょう。
5.1. ベンチマークテスト
ArrayList’ s contains()メソッドの最初のマイクロベンチマークを追加しましょう。
@Benchmark
public boolean containsArrayList() {
return arrayList.contains(getValue);
}
同様に、Troveの TIntArrayList の場合、 contains()ベンチマークは次のようになります。
@Benchmark
public boolean containsTroveIntList() {
return tList.contains(getValue);
}
同様に、Coltの cern.colt.list.IntArrayList.contains()のテストは次のとおりです。
@Benchmark
public boolean containsColtIntList() {
return coltList.contains(getValue);
}
また、Fastutilの 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
いつものように、containsArrayListメソッドのパフォーマンスは最低です。 対照的に、Trove、Colt、およびFastutilは、Javaのコアソリューションと比較してパフォーマンスが優れています。
今回は、どのライブラリを選択するかは開発者次第です。 3つのライブラリすべての結果は、それらが同一であると見なすのに十分に近いものです。
6. 結論
この記事では、JVMベンチマークテストを通じてプリミティブリストの実際の実行時パフォーマンスを調査しました。 さらに、テスト結果をJDKのArrayListと比較しました。
また、ここに示す数値は単なるJMHベンチマーク結果であることに注意してください。常に、特定のシステムとランタイムの範囲でテストしてください。
いつものように、この記事の完全なコードは、GitHubでから入手できます。