2011-12-22 10 views
2

ConcurrentHashMapをキャッシュとして使用する株式市場シミュレータを作成しました。高性能キャッシュの作成

キャッシュは約75個の要素を保持しますが、キャッシュは非常に高速に更新され(約500回/秒)更新されます。ここで

は私がやったことです:

スレッド1:

与えられた銘柄記号のためのストリーミング引用符で私を提供外部のシステムに接続されています。

スレッド2(コールバックスレッド):

待機データは、外部システムによってそこに配信されるまで。データを取得すると、それを解析し、不変のDataEntryオブジェクトを作成し、キャッシュし、thread3に信号を送信します。

スレッド3(消費者スレッド): 信号を受信すると、キャッシュからDataEntryを取り出して使用します。 (これは、スレッド2がスレッド3に直接データをプッシュしないようにするタスクの一部です)。

public final class DataEntry{ 

     private final String field1; 
     private final String field2; 
     //... 
     private final String field25; 

     // Corresponding setters and getters 

} 

public final class Cache{ 

     private final Map<String, DataEntry> cache; 

     public Cache(){ 
      this.cache = new ConcurrentHashMap<String, DataEntry> (65, 0.75, 32); 
     } 

     // Methods to update and retrieve DataEntry from the cache. 
} 

プロファイラを通してそれを実行した後、私は私がたくさんDataEntryオブジェクトのを作成していていることに気づきました。したがって、エデンは非常に迅速に満ちています。 A)がDataEntryクラスが変更可能な作り

だから、私はしてデザインを少し微調整を考えています。

b)キャッシュを空のDataEntryオブジェクトに事前に移入します。

c)更新が到着したら、DataEntryオブジェクトをマップから取得し、フィールドに入力します。

このようにして、DataEntryオブジェクトの数は一定で、要素数に等しくなります。

私の質問は以下のとおりです。

A)が、このデザインは、私がDataEntryが可変することによって導入されている可能性のある並行性の問題を持っています。

b)キャッシュを最適化するために何かできることはありますか?

ありがとうございました。

+0

ConcurrentHasMapには毎秒100万回以上アクセスでき、500にしかアクセスしていない場合はそれほど大きな影響はありません回/秒。 –

+0

Mapに一度にアクセスするコア数が16以上になると予想される場合は、パーティションサイズを増やすだけです。一度に地図にアクセスする(そして他の何もしない)4コア未満を使用している場合は、それほど大きな違いはありません。 –

+0

エデンをすぐにいっぱいにするのはなぜ問題なのですか?実際にEden GCに問題がありますか? – kdgregory

答えて

1

私はConcurrentHashMapの

Map<Integer, Integer> map = new ConcurrentHashMap<>(); 
long start = System.nanoTime(); 
int runs = 200*1000*1000; 
for (int r = 0; r < runs; r++) { 
    map.put(r & 127, r & 127); 
    map.get((~r) & 127); 
} 
long time = System.nanoTime() - start; 
System.out.printf("Throughput of %.1f million accesses per second%n", 
     2 * runs/1e6/(time/1e9)); 

の速さを心配しないでしょうプリント

Throughput of 72.6 million accesses per second 

これは、はるかにあなたが使用しているように思われるアクセス速度を超えています。

ガベージを減らしたい場合は、変更可能なオブジェクトとプリミティブを使用できます。この理由から、Stringを使用することは避けてください(データエントリよりもはるかに文字列が多いようです)

+0

Peterに感謝します。もう1つの質問ですが、DataEntryを変更可能にするとマップをロックしなくてはなりません。または、私はAtomicReferenceで変更可能なDataEntryをラップできますか?乾杯(あなたのブログを愛しています)。 – CaptainHastings

+1

並行性が高いシステムの場合、デフォルトの同時ハッシュマップは良い考えではありませんが、この説明から、これはおそらくそうではないようです(私はYMMVと並行して数十以上のスレッドを持つものを考えています) 。この説明から、ハッシュマップは、とにかく奇妙な選択と思われます。 – Voo

+0

変更可能なDataEntryを使用している場合は、一度だけ追加します(できれば起動時に)。ブログについての感謝。 ;) –

1

が本当にになる必要があるときは、ConcurrentHashMapを使用しているようですが、これは並行キューのようなものです - たとえば、LinkedBlockingQueue

+0

こんにちは、実際に私は地図を必要とします。関係者に新しいデータをポーリングするのではなく、新しいデータをポーリングする必要があるからです。 – CaptainHastings

+1

ええと...私は地図がまだそれにとって最高のデータ構造であるかどうかはわかりません。それぞれの "関心のあるパーティー"ごとに別々のキューを使うことについて考えましたか? –

1
  • a。はい、そうです。Mutable DataEntryオブジェクトは、読者が気づかずに更新される可能性があり、不整合な状態につながります。
  • b。はい、可能です:変更可能なDataEntryを要求に応じて返す変更可能なDataEntryCacheを作成します。この方法では、書き込み時ではなく、読み取り時に新しいDataEntryオブジェクトを作成します。 DataEntryCacheは、それが構成して返す不変のDataEntryを内部的にキャッシュし、突然変異する呼び出しでその「キャッシュ」を無効にできます。

編集:キャッシュする理由(スレッド2とスレッド3の間にキューを作成するのとは対照的に)は、コンシューマスレッドがスレッド2が他のエントリを読み取る可能性があることを前提としています。お知らせ。この仮定が間違っていると、キャッシュがまったく必要ない場合があります。私のコードオブジェクトの作成で

+0

+1、bへのあなたの答えは私にとって競合状態のように見えますが – kdgregory

+0

こんにちは、私はマップを必要とします。 – CaptainHastings

+0

@kdgregoryこの操作は 'DataEntryCache'の内部で同期する必要があります。 – dasblinkenlight

0

A)は、多くの場合、ボトルネックとして現れたので、私はDataEntryオブジェクトを再利用する独自のアイデアがあまりにも実装する価値があると思います。しかし、kdgregoryがコメントしたように、現在の要素を上書きするだけでは、一貫性のないエントリが読み取られることになります。したがって、エントリを更新するときには、新しいエントリまたは使用可能な場合は、再利用されたアイドル(数分間のアイドル)エントリを書き込んで、マップに書き込んでください。新しいエントリをマップに入れたら、古いエントリを何らかの種類のアイドルリストに置きます。完全に安全であるためには、読み取りスレッドは、キャッシュによって配信されたDataEntryにアクセスできないようにする必要があります。 1分。スレッドがブロックする可能性がある場合は、DataEntryオブジェクトをコピーする必要があります。おそらく、このために独自のオブジェクトを再利用してください。

b)現在のデザインはモジュール式ですが、スレッドにはモジュールが反映されているため、多くのコンテキストスイッチが必要です。私は、単一の要求が単一のスレッドによって最初から最後まで提供される設計を試みるだろう。要求は新しいDataEntryオブジェクトの完全な処理である可能性があります。これを実現する並行処理設計パターンはLeader/FollowerHalf-Sync/Half-Asynchです。

+0

"明らかな並行性の問題はありません" - スレッドが値を書き込んでいる間にスレッドがオブジェクトから値を読み取っている場合はどうなりますか? – kdgregory

+0

リンクをありがとう、今日読んでいただきます。 – CaptainHastings

+0

@kalegory、ありがとう。それは明らかだった*)。私は自分の答えを更新しました。 –