3

据我了解,对IORefs 的修改非常快,它们所涉及的只是更新一个 thunk 指针。当然,读者(即想要在他们的网页上看到价值的人)将需要花时间来评估这些 thunk(如果作者没有回读结果,这可能会增加)。

我认为最好开始IORef并行地实际评估修改 thunk,因为在许多情况下,它们可能无论如何都必须在某个时候进行评估(显然,这将破坏无限的数据结构)。

因此,我编写了以下函数,其签名类似于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

这似乎有效(测试代码here)。我在这里做错了什么吗?还是有更好的方法来做到这一点?


编辑:第二次尝试

受以下卡尔回答的启发。我们实际上存储force newdataIORef. 这和反正是一样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 的版本。火花池与 GC 的交互在历史上一直是可变的。在某些版本中,表达式在返回force newdata后没有被范围内的任何东西引用这一事实atomicModifyIORefPar意味着它很可能在parever创建的火花被转换之前被垃圾收集,这意味着火花也将被收集。

其他版本的 GHC 将火花池视为 GC 分析的根源,但这也存在问题。我不记得当前状态是什么,但我怀疑火花池不算作 GC 根。它引发的问题(当返回的表达式不引用并行计算的表达式时失去并行性)比将火花池视为 GC 根(保留不需要的内存)所产生的问题要好。


编辑 - 第二次尝试回答

由于您给出的原因,这个新的实现看起来是正确的。并行计算的表达式也可以从 GC 根中访问。

于 2012-04-18T15:47:08.993 回答