Haskell:尝试并行 `atomicModifyIORef` 实现

标签 haskell concurrency ioref

据我了解,对 IORef 的修改非常快,它们所涉及的只是更新一个 thunk 指针。当然,读者(即想要在其网页上看到值(value)的人)将需要花时间来评估这些重击(如果作者不读回结果,则可能会累积)。

我认为最好开始并行地实际评估 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

这似乎有效( test code here )。我在这里做错了什么吗?或者有更好的方法吗?

<小时/>

编辑:第二次尝试

灵感来自Carl's answer below 。我们实际上将force newdata存储到IORef中。无论如何,这与 newdata 相同,但显示了我们希望为以后保留force newdata 的运行时,因此它不会对 Spark 进行垃圾收集。

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

最佳答案

这可能有效,也可能无效,具体取决于 GHC 的版本。 Spark 池与 GC 的交互在整个历史中一直是变化的。在某些版本中,事实上,在 atomicModifyIORefPar 返回后,表达式 force newdata 没有被范围内的任何内容引用,这意味着它很可能在由parever 被转换,这意味着 Spark 也将被收集。

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

<小时/>

编辑 - 第二次尝试回答

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

关于Haskell:尝试并行 `atomicModifyIORef` 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10203446/

相关文章:

haskell - 如何在 Haskell 中实现 fork try-catch?

android - 我怎样才能同步订阅一个可观察对象,这样我就不会错过该可观察对象的发射?

C++。多线程瓶颈到带有列表的单线程应用程序

multithreading - IORef 和 MVar 有什么区别?

haskell - fork 的 IORef 读取器函数似乎会停止主线程

haskell - 何时使用 STRef 或 IORef?

haskell - 什么时候使用 ByteString,什么时候不使用?

haskell - 衡量绩效的方法

haskell - 是否可以使用类型族而不是fundeps来实现Data.Reflection的技巧?

java - Java 中非最终对象同步的正确方法