2012-03-18 13 views
3

私は1つのプロデューサ、Mコンシューマスレッドパターンを持っています。プロデューサは、ディスクから未処理のドキュメントを読み取り、LinkedBlockingQueueに配置します。マルチスレッドJava正規表現

public String clearContent(String document) { 
    Pattern regex = Pattern.compile(pattern); 
    Matcher matcher = regex.matcher(document); 
    matcher.find(); 
    .... 
} 

public String removeHTML(String document) { 
    Pattern regex = Pattern.compile(pattern); 
    Matcher matcher = regex.matcher(document); 
    matcher.replaceAll(""); 
    .... 
} 

私が直面しています問題がある:各コンシューマスレッドはその後、parseDocクラスは次のパターンで約20メソッドのセットです

ParsedDoc article = parseDoc(rawDocument); 

を生文書を受け取り、クラスを使用して文書を解析しますローカル(2コア)マシンでコードがかなり速く実行されることを確認します。しかし、私が8コアマシンで同じコードを実行すると、コンシューマーのパフォーマンスはほとんど低下してしまいます。私はjvmオプションを無駄に最適化しようとしました。正規表現の処理ステップを削除すると、8コアで予想されるx4のパフォーマンスが向上しました。だから問題は正規表現です。パターンはスレッドセーフであり、マッチャーはリセット()する必要があるかもしれません。しかし、問題は正規表現のバンクを(parseDocクラスで)M個のコンシューマ間でスレッドセーフであるように再設計する方法です。

任意の助けも

をいただければ幸いです

答えて

1

プロデューサー消費パターンがうまくスケールしない、ありがとうございました。 多くのプロデューサーやコンシューマーは、パフォーマンスが悪化しています。その理由は、共通キューがシステム全体のボトルネックになるからです。私はあなたがどのように見ることを望む。

もっと良い方法は共通キューを持たないことです。各消費者に独自のキューを持たせる。要求が到着すると、ロードバランサに送られます。ロードバランサは最小のコンシューマキューに要求を格納します。バランサーはボトルネックになりますが、多くの操作を実行するわけではありません。着信要求を送信するために適切なキューを選択するだけで、高速に処理されます。 Problem (more in depth):あなたは遅く、それが取得する必要があり、よりコア

はここにあなたの質問に答えるために編集です。どうして?共有メモリ。

@ペイマン使用ConcurrentLinkedQueue(1つのエンキューと1つのデキューを並行して処理できる非ブロッキング待機フリーキュー)です。あなたの最初のデザインでそれを試しても、両方のデザインをベンチマークしてください。最初のデザインと同じようにエンキューとデキューを1回ではなく、同時に1つのエンキューと1つのデキューを行うことができるため、改良された設計がより効果的に実行されることを期待しています。

great paper on scalable consumer producer by using balancers

読むthis page(または唯一の「キュー・スレッドごとのアプローチに共通するワーカーキュー・アプローチからの移行」を見ることができます)

ここhttp://www.javaperformancetuning.com/news/newtips128.shtmlからのリストです。私は最後の3ポイントはあなたに、より適用と思います:

  • ほとんどのサーバアプリケーションが共通のワーカーキューとスレッドプールを使用します。共有ワーカーキューには、リモートソースから到着する短いタスクが格納されます。スレッドのプールはキューからタスクを取得し、タスクを処理します。処理するタスクがない場合、スレッドはキュー上でブロックされます。
  • タスク数が多く、タスク時間が非常に短い場合、スレッド間で共有されるフィーダキューは、(競合の)アクセスボトルネックです。ボトルネックは、使用されるコアが増えるほど悪化します。
  • 共有キューにアクセスする際の競合を解決するために使用できるソリューションには次のものがあります。ロックフリーのデータ構造を使用する。複数のロックを持つ同時データ構造の使用。複数のキューを維持して競合を分離する
  • キューごとのアプローチは、キューアクセスの競合を排除しますが、未処理のキューに入れられたデータが他のキューにある間にキューが空になると最適ではありません。これを改善するために、アイドル状態のスレッドは他のキューから作業を奪うことができます。競合を最小限に抑えるには、他のキューの末尾(スレッド自体のキューからの通常のデキューがキューの先頭から実行される)から「スチール」を実行する必要があります。
+1

、タスク(すなわち、32/64ビット・ポインタ/参照)をポップ/プッシュするのにかかる時間は、キューからタスク&ので、競合を処理するのに要する時間よりもはるかに小さい、くらいです問題はありません。開発者が2つの整数を足し合わせたP-Cキューにジョブをプッシュしようとしているなら、それはうまく拡張できません。任意の合理的なデータセットのパターンマッチングは、プールへのスレッドオフには問題ありません。 –

+1

一般的なブロッキングキューへのプッシュ/ポップよりも明らかに時間がかかるロードバランサの例はありますか?こんにちはエイドリアン。 –

+0

私はロードバランサを正しく実装しているかどうかはわかりませんが、あなたの提案に従っています。残念ながら私は改善が見られません。実際、それは8コアマシンで悪化します。各スレッドはローカルのLinkedListキューを持ち、プロデューサスレッドはそのラインを読み込み、ロードバランシングを行い、各スレッドのキューにどれくらいの負荷がかかっているかを判断します。ノンブロッキングは各コンシューマスレッドの正しいデータ構造になっていますか? – Peyman

1

正規表現のコンパイルが遅いです。あなたは与えられたパターンに対して一度だけ行うべきです。あなたのサンプルに表示されるpattern変数が、呼び出しごとに本当に異なる場合を除き、Patternのインスタンスはおそらくstaticのクラスメンバになる可能性があります。 Patternは、複数のスレッドによる同時使用に対して明示的に安全です。 (すべての変更可能な状態はMatcherによって保持されます)

Matcherは単一スレッドのスタックに限定されているので、気にするスレッドの問題はありません。 Matcherを再利用しようとしないでください。それはできますが、正規表現のコンパイルと比べてリサイクルすると時間が大幅に節約できれば驚いています。

+0

こんにちはエリック。私は、提案されたようにコンパイルされた正規表現をクラスから移動させようとしましたが、利益はあまりありませんでした。 – Peyman

+0

8つのコアすべてで、プロファイリングがCPU使用率が100%に固定されていることを示していますか?消費者がプロデューサーがディスクから別のファイルを読み取るのを待っているだけではないことを確認できますか? – erickson

0

制御から外れている同期化のために期待している並行性が得られない場合は、コア数-1までサブプロセス(追加のJVM)に作業をディスパッチしてください。ほとんどの場合