【问题标题】:Forcing evaluation across an IORef: rnf, deepSeq, or something else?强制对 IORef 进行评估:rnf、deepSeq 还是其他?
【发布时间】:2011-07-12 10:11:20
【问题描述】:

我有一个长时间运行的进程,它是forkIO'd,它产生像素颜色值:

takesAgesToRun :: [[Color]]

myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
                         writeIORef ref colors

(其中Color 仅包含三个Double 值)。

正如预期的那样,当在IORef 的“另一边”读取时,存储的值只是一个 thunk,因此阻塞了主进程。

我知道我需要全面评估 [[Color]] 值以引导范式,但似乎有两种方法可以实现这一点,此外,我不确定如何将其中任何一种合并到我的代码中。

我该怎么做?我是使用rnfdeepSeq 还是其他一些线程策略? 是首选其中之一,而其他则弃用了吗?它如何适合我的代码?

(PS 请忽略将图像存储为颜色列表的事实是愚蠢的 - 这只是代码的简化版本)。

【问题讨论】:

  • 请注意,如果您使用 [[Color]] 来呈现像素,那么无论您将其并行化多少,您的程序的性能都可能很糟糕。
  • @tibbe - 是的,同意。我对 Haskell 非常陌生,所以除了让程序真正运行之外,我的下一个目标是保持应用程序的 UI 响应。稍后我会将位图存储移动到 UArray 或类似位置 - 但无论如何我仍然需要此功能。
  • 还要考虑向量库中未装箱的向量。我一直在搞乱这些,它们似乎设计得更好,性能也更好;)
  • @stusmith - 尽管您的决定背后的原则是明智的,但我建议您尽早转向替代存储实现。由于列表与数组和向量具有不同的结构,因此您将无法准确衡量您的算法。您将编写旨在使用数组并在列表中获得可怕性能的算法,或者您将编写针对在其他结构上具有次优结果的列表进行调整的算法。如果您使用兼容的存储,它还将简化与其他库的接口。
  • @monadic:我从未见过未装箱的向量比数组有更好的性能,你能在某处发布一个例子吗?我不认为有任何问题,界面更丰富。

标签: multithreading haskell ioref


【解决方案1】:

使用deepSeq。它就像seq 一样使用。你可以像这样合并它:

myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
                         deepSeq colors $ writeIORef ref colors

这将强制在“writeIORef”调用之前对“颜色”进行全面评估。

为了使其工作,您将需要ColorNFData 实例。具体怎么写取决于Color的定义,这里举两个例子:

-- just for reference
data Color = Color Double Double Double

instance NFData Color where
    rnf (Color r g b) = r `seq` g `seq` b `seq` ()

-- closer to the likely actual implementation for Color
data Color2 = Color2 !Double !Double !Double

instance NFData  Color2 where
-- the default implementation is fine

对于Color 实例,您需要确保颜色的所有组件都被完全评估[1],只要颜色是。这就是seqs 所做的。我们可以在这里使用seq而不是deepSeq,因为我们知道每个组件都是一个Double,因此被seq完全评估。如果组件是更复杂的数据类型,那么我们在编写 NFData 实例时需要使用deepSeq

Color2 中它更简单一些。由于 bang 模式,我们知道当 Color2 是时,组件被完全评估。这意味着我们可以使用默认实现,它将 Color2 评估为弱头部正常形式,由于 bang 模式被完全评估。

rnf 主要在与 Control.Parallel.Strategies 结合使用时有用。这是deepSeq的当前定义

deepseq :: NFData a => a -> b -> b
deepseq a b = rnf a `seq` b

所有 deepseq 所做的就是调用 rnf 并保证其输出 () 被评估。这确实是直接使用rnf的唯一方法。

[1] Haskell 只提供了两种评估东西的通用方法:模式匹配和seq。其他一切都建立在其中之一或两者之上。对于 NFData Color 实例,首先通过与 Color 构造函数的模式匹配将 Color 评估为 WHNF,然后通过 seq 评估组件。

当然还有第三种高度专业化的方法来评估东西:即一个函数main :: IO ()将被执行以评估()

【讨论】:

  • 非常感谢!这对某些人来说似乎都是显而易见的东西,但在网上搜索会发现很多关于这类事情的讨论,但不是它如何实际应用于实际代码(或者至少,我有限的能力无法理解)。也许有一天会有第二卷“Learn You a Haskell...”。
  • 之前忘了提这个,但如果你要分叉进程,最好使用 MVar 或 STM 而不是 IOVar。并非 IOVar 上的所有操作都保证是原子的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
  • 2012-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多