7

私は並行性を理解しようとしてきました。そして、1つの大きなIORefロック、または多くTVarのロックのどちらが優れているかを理解しようとしています。私は以下のガイドラインに到達しました。これらが大まかに正しいかどうか、または私が要点を見逃したかどうかについて、コメントをいただければ幸いです。


同時データ構造がマップmであり、のようにアクセスされると仮定しましょうm[i]f_easyまた、2つの関数があるとしましょうf_hard。はf_easy速く、f_hard時間がかかります。f_easy/f_hardの引数はの要素であると想定しますm

(1)トランザクションがおおよそこのように見える場合は、 withをm[f_easy(...)] = f_hard(...)使用します。怠惰は、サンクで更新されるため、短時間だけロックされるようにします。インデックスを計算すると、構造が効果的にロックされます(何かが更新されるためですが、まだ何がわかりません)が、その要素が何であるかがわかると、構造全体のサンクはその特定の要素のサンクにのみ移動します、そしてその特定の要素のみが「ロック」されます。IORefatomicModifyIORefm

(2)トランザクションがおおよそこのようm[f_hard(...)] = f_easy(...)になり、競合があまり発生しない場合は、をたくさん使用しますTVar。この場合にを使用するIORefと、2つのインデックスを同時に計算できないため、アプリが効果的にシングルスレッドになります(構造全体に未解決のサンクが存在するため)。TVar■2つのインデックスを同時に処理できますが、2つの同時トランザクションが両方とも同じ要素にアクセスし、そのうちの1つが書き込みである場合、1つのトランザクションを破棄する必要があり、時間が無駄になります(他の場所で使用)。これが頻繁に発生する場合は、(ブラックホールを介して)から来るロックを使用した方がよい場合がありますが、IORefあまり発生しない場合は、TVarsを使用した方が並列処理が向上します。

基本的に(2)の場合、IORef100%の効率(無駄な作業なし)が得られる可能性がありますが、1.1スレッドのみを使用しTVarますが、競合の数が少ない場合は、80%の効率が得られる可能性がありますが、10スレッドを使用するため、終了します。無駄な作業でも7倍速くなります。

4

2 に答える 2

5

あなたのガイドラインは、Haskell STMのパフォーマンスが分析されている[1](セクション6)の結果にいくぶん似ています。

「特に、トランザクション内で多くの作業を実行しないプログラムの場合、コミットのオーバーヘッドは非常に高いように見えます。このオーバーヘッドをさらに観察するには、コミット時間のコースグレインとファイングレインのパフォーマンスを分析する必要があります。 STMロックメカニズム。」

私が使用するatomicModifyIORef、またはMVar必要なすべての同期が単純なロックによって保証されるものである場合。データ構造への同時アクセスを見るとき、それはこのデータ構造がどのように実装されているかも依存します。たとえば、データをa内に格納し、IORef Data.Map読み取り/書き込みアクセスを頻繁に実行する場合、推測どおり、シングルスレッドのパフォーマンスが低下すると思いますが、。atmoicModifyIORefについても同じことが言えますTVar Data.Map。私のポイントは、並行プログラミングに適したデータ構造を使用することが重要であるということです(バランスの取れたツリーはそうではありません)。

とは言うものの、私の意見では、STMを使用するための最も重要な議論は構成可能性です。つまり、頭痛の種なしに複数の操作を1つのトランザクションに組み合わせることができます。一般に、これは、新しいロックを導入 するIORefかどうかにかかわらず不可能です。MVar

[1]ソフトウェアトランザクショナルメモリ(STM)の限界:メニーコア環境でのHaskellSTMアプリケーションの分析。 http://dx.doi.org/10.1145/1366230.1366241

@クリントンのコメントへの回答:

シングル IORefすべてのデータが含まれている場合は、単純atomicModifyIORefに構成に使用できます。ただし、そのデータへの多数の並列読み取り/書き込み要求を処理する必要がある場合、そのデータへの並列読み取り/書き込み要求のすべてのペアが競合を引き起こす可能性があるため、パフォーマンスの低下が大きくなる可能性があります。

私が試みるアプローチは、エントリ自体がa内に格納されているデータ構造を使用することですTVar(データ構造全体を1つにまとめるのとは異なりますTVar)。トランザクションがそれほど頻繁に競合しないため、これによりライブロックの可能性が減少するはずです。

もちろん、トランザクションを可能な限り小さくし、一貫性を保証することが絶対に必要な場合にのみ構成可能性を使用する必要があります。これまでのところ、複数の挿入/ルックアップ操作を1つのトランザクションに組み合わせる必要があるシナリオは発生していません。

于 2012-04-19T02:45:29.707 に答える
1

パフォーマンスを超えて、使用するより根本的な理由がわかります。型システムは、またはTVarのような「安全でない」操作を行わないことを保証します。データが共有されることは、実装ではなく、タイプのプロパティです。編集:常に安全ではありません。 も使用している場合にのみ安全ではありません。少なくとも、IORefをニュータイプでラップし、ラップされたものだけを公開しますreadIORefwriteIORefunsafePerformIOreadIORefatomicModifyIORefatomicModifyIORef

それを超えて、使用しないでくださいIORef、使用MVarまたはTVar

  1. あなたが説明する最初の使用パターンは、おそらく優れたパフォーマンス特性を持っていません。おそらく(ほぼ)完全にシングルスレッドになる可能性があります-怠惰のため、共有状態を更新するたびに実際の作業は発生しませんが、この共有状態を使用する必要があるときはいつでも、蓄積されたサンクの山全体を強制する必要があります。線形データ依存構造を持っています。
  2. 効率は80%ですが、並列処理が大幅に高いため、増え続けるコアを活用できます。シングルスレッドコードでは、今後数年間で最小限のパフォーマンスの向上が期待できます。
  3. 多くの単語CASは、STMをはるかに効率的にすることを可能にする「ハードウェアトランザクションメモリ」の形であなたの近くのプロセッサに来る可能性があります。
  4. コードはよりモジュール化されます。デザインのすべての共有状態が単一の参照の背後にある場合に共有状態を追加する場合は、すべてのコードを変更する必要があります。 TVars程度は低いですが、MVars自然なモジュール性をサポートします。
于 2012-04-19T02:39:53.880 に答える