【问题标题】:Data.Sequence.Seq lazy parallel Functor instanceData.Sequence.Seq 惰性并行 Functor 实例
【发布时间】:2015-09-24 07:07:00
【问题描述】:

我需要来自Data.Sequence 包的Seqfmap 的并行(但懒惰)版本。但是包不会导出任何Seq 数据构造函数。所以我不能只将它包装在newtype 中并直接为newtype 实现Functor

我可以不重写整个包吗?

【问题讨论】:

  • “并行(但懒惰)”是什么意思?这些对我来说似乎是矛盾的。无论如何,我的建议是将通常的fmap 应用于Seq,然后对结果进行任何你想要的并行评估。
  • 我的意思是懒惰:or $ odd `pfmap` (fromList [4, 3, undefined]) -- => True

标签: haskell parallel-processing lazy-evaluation finger-tree


【解决方案1】:

你能做的最好的可能是将splitAt 序列分成块,fmap 覆盖每个块,然后将这些片段重新组合在一起。 Seq 被表示为finger tree,因此它的底层结构并不是特别适合并行算法——如果你按照它的自然结构来拆分它,连续的线程将变得越来越大。如果你想试一试,可以从Data.Sequence 源中复制FingerTree 类型的定义,并使用unsafeCoerce 在它和Seq 之间进行转换。您可能希望将前几个Deep 节点发送到一个线程,但随后您必须仔细考虑其余部分。手指树可能离权重平衡很远,主要是因为3^n 的渐近增长速度比2^n 快;您需要考虑到这一点以平衡线程之间的工作。

假设您使用splitAt,至少有两种合理的方式来拆分序列:

  1. 在将计算分解为线程之前将其全部拆分。如果你这样做,你应该从左到右或从右到左拆分它,因为拆分小块比先拆分大块然后再拆分更便宜。您应该以类似的方式附加结果。

  2. 在多个线程中递归拆分。如果您想要很多块或更多潜在的懒惰,这可能是有道理的。拆分靠近中间的列表并将每个部分发送到一个线程以进行进一步拆分和处理。

还有另一种可能更好的拆分方法,使用当前用于实现 zipWith 的机器(请参阅我提交的请求 chunksOf 的 GitHub 票证),但我不知道你会从中获得巨大的好处这个应用程序。

您寻求的非严格行为似乎不太可能在一般情况下起作用。您可能可以使其在许多或大多数特定情况下工作,但我不太乐观地认为您会找到一种完全通用的方法。

【讨论】:

    【解决方案2】:

    我找到了一个解决方案,但实际上效率并不高。

    -- | A combination of 'parTraversable' and 'fmap', encapsulating a common pattern:
    --
    -- > parFmap strat f = withStrategy (parTraversable strat) . fmap f
    --
    parFmap         :: Traversable t => Strategy b -> (a -> b) -> t a -> t b
    parFmap strat f = (`using` parTraversable strat) . fmap f
    
    -- | Parallel version of '<$>'
    (<$|>) :: Traversable t =>  (a -> b) -> t a -> t b
    (<$|>) = parFmap rpar
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-09
      • 2017-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多