ライブラリトローブ。 Javaのプリミティブ型コレクション

標準のJavaライブラリには、int、longなどプリミティブ型のコレクションを処理する機能がありません。 標準的な方法は、Integer、Longなどのクラスのオブジェクトを使用することです。

このアプローチは少数の要素でうまく機能します。これは、最初にすべての操作中にオートボクシング/オートボックス解除が発生し、次にヒープ内のオブジェクトへの参照がコレクションに格納されるためです。 ヒープ内のオブジェクトは、メモリからのオーバーヘッドを増やすだけでなく、 GCに負荷をかけます。

ヒープ内のオブジェクトには明らかなマイナスがもう1つあります。最新のプロセッサでのキャッシュです。 プロセッサは、ブロック単位でキャッシュにデータをロードします。 配列の順次処理の場合、いくつかの要素が一度にキャッシュにロードされます。 ヒープ上に散在するオブジェクトの場合、キャッシュ内のヒットは少なくなります。 キャッシュとメモリ階層の詳細については、 こちらをご覧ください

Troveライブラリは、プリミティブ型を操作するための標準コレクションインターフェイスを提供します。 ほとんどのアプリケーションでは、Troveコレクションの方が高速であり、メモリの消費も少なくなります。

コレクションセットには以下が含まれます。

jdkとは異なり、Trove HashはOpen addressing collision resolutionメソッドを使用します。

命名原則はT <Type> <CollectionType>です。
例:
TIntListインターフェイス-intのリスト、TIntArrayListの実装:
TIntList l = new TIntArrayList(); 

TLongLongMap-長いキーと長い値を持つマップインターフェイス、TLongLongHashMapの実装:
 TLongLongMap m = new TLongLongHashMap(); 

jdkコレクションでは、アイテムが見つからない場合、nullが返されます。 「NoEntryValue」はTroveに戻ります。デフォルトは0です。コレクションの作成時にNoEntryValueを学習し、 NoEntryValueを設定できます。

Troveコレクションの利点は処理方法です-forEach、
  public static long troveEach() { final long [] rvalue = {0}; // troveMap - TLongLongHashMap // TLongProcedure      , //    false //     troveMap.forEachValue(new TLongProcedure() { @Override public boolean execute(long value) { rvalue[0] += value; return true; } }); return rvalue[0]; } 

grepinverseGrep-条件付きリストのコンパイル (TList ...)およびtransformValues-コレクション要素のインプレース変更操作。

便利な機能-オブジェクト(オブジェクト継承)をキーとして持つHas​​hMap / HashSetの場合、ハッシュ関数Interface HashingStrategy <T>を使用できます。

ベンチマークには、優れたベンチマークツールjmhを使用しました。
開発者がそれをMavenリポジトリに公開してくれたら素晴らしいと思います。

書式設定を維持するために、出力をわずかに編集する必要がありました。1つの操作-100 要素( 完全なレポートはこちら ):
 $ java -version java version "1.7.0_21" Java(TM) SE Runtime Environment (build 1.7.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) $ java -server -XX:+AggressiveOpts -Xms2048m \ -Xmx2048m -jar microbenchmarks.jar ".*Trove.*" -prof gc -i 3 -r 5s 

ベンチマークモードThr Cnt Sec Mean Mean error Units
 //リストに挿入<整数>
 IntListJdkInsert thrpt 1 3 5 104.950 6.756 ops /秒
 //リストの列挙を完了<整数>
 IntListJdkTraverse thrpt 1 3 5 774.100 71.809 ops /秒

 // TIntArrayListに挿入します
 IntListTroveInsert thrpt 1 3 5 424.556 28.239 ops /秒
 //完全な列挙TIntArrayList
 IntListTroveTraverse thrpt 1 3 5 3548.806 7.712 ops /秒

 // HashMapに挿入<Long、Long>
 LongHashMapJdkInsert thrpt 1 3 5 24.683 1.994 ops /秒
 // HashMap <Long、Long>内のすべての要素を順番に検索します
 LongHashMapJdkSearch thrpt 1 3 5 67.789 1.119 ops /秒
 //値の徹底的な検索HashMap <Long、Long>
 LongHashMapJdkTraverse thrpt 1 3 5 99.761 0.882 ops /秒

 // TLongLongMapに挿入
 LongHashMapTroveInsert thrpt 1 3 5 28.750 0.165 ops /秒
 // TLongLongMapのすべての要素を順番に検索します
 LongHashMapTroveSearch thrpt 1 3 5 145.933 0.416 ops /秒
 // forEachを使用したTLongLongMap値の完全な列挙
 LongHashMapTroveTraverse thrpt 1 3 5 318.528 0.980 ops /秒

占有メモリの量を計算するのはそれほど簡単ではありませんが、GCアクティビティから間接的な結論を引き出すことができます。
  jd  List<Integer>: Iteration 1 (5s in 1 thread): 103,950 ops/sec GC | wall time = 5,002 secs, GC time = 0,331 secs, GC% = 6,62%, GC count = +24  Trove  TIntArrayList: Iteration 1 (5s in 1 thread): 428,400 ops/sec GC | wall time = 5,002 secs, GC time = 0,019 secs, GC% = 0,38%, GC count = +32 

TIntArrayListのソースを見ると、パフォーマンスの向上がどこから得られるかが明らかになります。データは配列として保存されます。
 public class TIntArrayList implements TIntList, Externalizable { static final long serialVersionUID = 1L; /** the data of the list */ protected int[] _data; 

TLongLongMapの検索速度はforEachを使用して説明されます-一時オブジェクトは作成されず、ボックス化解除は除外されます。

同じベンチマークですが、1つの操作-1000の要素:
ベンチマークモードThr Cnt Sec Mean Mean error Units
 IntListJdkInsert thrpt 1 3 5 239478.011 871.469 ops /秒
 IntListJdkTraverse thrpt 1 3 5 1326701.717 1649.389 ops /秒

 IntListTroveInsert thrpt 1 3 5 315562.594 2483.415 ops /秒
 IntListTroveTraverse thrpt 1 3 5 3630599.806 10822.903 ops /秒

 LongHashMapJdkInsert thrpt 1 3 5 45315.689 47.630 ops /秒
 LongHashMapJdkSearch thrpt 1 3 5 114759.789 424.996 ops /秒
 LongHashMapJdkTraverse thrpt 1 3 5 210012.550 139.001 ops /秒

 LongHashMapTroveInsert thrpt 1 3 5 33078.583 119.127 ops /秒
 LongHashMapTroveSearch thrpt 1 3 5 148311.567 267.613 ops /秒
 LongHashMapTroveTraverse thrpt 1 3 5 351955.550 901.902 ops /秒

コレクション内の要素の数を減らすと、パフォーマンスのギャップが小さくなることがわかります。

コレクションが小さく、使用頻度が低い場合、プロジェクトに追加の依存関係を含めることに特別な意味はないと結論付けることができます。
しかし、プロジェクトがプリミティブ型の大規模なコレクションを大量に使用する場合は、Troveの使用を正当化できます。

プロジェクトコードはGitHubで入手できます

PS。 コレクションを作成するときの要素数の値は、意図的に設定されていません。

Source: https://habr.com/ru/post/J187234/


All Articles