根据我的理解,对IORef
的修改非常快,所有它们都涉及更新thunk指针。当然,读者(即想要在他们的网页上看到价值的人)将需要花时间来评估这些thunk(如果作者没有回读结果,这可能会积累。)
我认为开始实际评估IORef
上的修改thunks是很好的,因为在许多情况下它们可能必须在某些时候进行评估(显然,这会打破无限的数据结构)。
所以我编写了以下函数,其签名与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
的运行时,因此它不会垃圾收集火花。
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
答案 0 :(得分:4)
根据GHC的版本,这可能有效,也可能无效。火花池与GC的相互作用在整个历史中都是可变的。在某些版本中,force newdata
返回后范围内的任何内容都未引用表达式atomicModifyIORefPar
这一事实意味着它可能在par
创建的火花之前被垃圾收集。转换,这意味着火花也将被收集。
其他版本的GHC已将火花池视为GC分析的根源,但这也存在问题。我不记得当前状态是什么,但我怀疑它是火花池不算作GC的根源。它引发的问题(当返回的表达式不引用并行计算的表达式时失去并行性)比将Spark池视为GC根(保留不需要的内存)所产生的问题要小得多。 / p>
编辑 - 第二次尝试回答
由于您提供的原因,这个新的实施看起来是正确的。并行计算的表达式也可以从GC根源获得。