3

私が理解していることから、IORefs への変更は非常に迅速であり、サンクポインターを更新するだけです。もちろん、読者 (つまり、自分の Web ページで値を確認したい人) は、時間をかけてこれらのサンクを評価する必要があります (ライターが結果を読み戻していない場合、サンクが蓄積される可能性があります)。

IORef多くの状況では、いずれにせよ何らかの時点で評価する必要があるためです (明らかに、これは無限のデータ構造で壊れます) 。

そこで、 と同様のシグネチャを使用して、次の関数を作成しましたatomicModifyIORef

atomicModifyIORefPar :: (NFData a) => IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORefPar ioref f =
  let 
    g olddata = 
      let (newdata, result) = f olddata in (newdata, (result, newdata))
  in do
    (result, newdata) <- atomicModifyIORef ioref g
    force newdata `par` return result

これはうまくいくようです(テストコードはこちら)。ここで何か間違ったことはありますか?または、これを行うより良い方法はありますか?


編集:2回目の試み

以下のカールの回答に触発されました。実際に に格納force newdataしますIORef。これはとにかく同じnewdataですが、後で保持したいランタイムを示しているforce newdataため、スパークのガベージ コレクションは行われません。

atomicModifyIORefPar :: (NFData a) => IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORefPar ioref f =
  let 
    g olddata = 
      let 
        (newdata, result) = f olddata
        newdata_forced = force newdata
      in 
        (newdata_forced, (result, newdata_forced))
  in do
    (result, newdata_forced) <- atomicModifyIORef ioref g
    newdata_forced `par` return result
4

1 に答える 1

4

GHC のバージョンによっては、これが機能する場合と機能しない場合があります。Spark プールと GC との相互作用は、歴史を通じて変化してきました。一部のバージョンでは、返さforce newdataれた後に式がスコープ内の何も参照されないという事実は、 everatomicModifyIORefParによって作成されたスパークが変換される前にガベージ コレクションされる可能性が高いことを意味します。これは、スパークも収集されることを意味します。par

GHC の他のバージョンでは、スパークプールを GC 分析のルートとして扱っていましたが、これにも問題があります。現在の状態は覚えていませんが、spark プールが GC ルートとしてカウントされていないことが原因だと思われます。それが引き起こす問題 (返された式が並列で評価される式を参照していない場合の並列性の喪失) は、spark プールを GC ルートとして扱う (不要なメモリを保持する) ことによって生じる問題よりも深刻ではありません。


編集 - 回答の 2 回目の試み

あなたが与えた理由から、この新しい実装は正しく見えます。並列で評価される式は、GC ルートからも到達可能です。

于 2012-04-18T15:47:08.993 に答える