加速数据类型的流

时间:2014-12-29 05:42:07

标签: performance haskell stream pipeline fold

我做了一个应该模仿"流"的类型。这基本上是没有记忆的列表。

data Stream a = forall s. Stream (s -> Maybe (a, s)) s

基本上,流有两个元素。状态s,以及获取状态的函数,并返回类型为a的元素和新状态。

我希望能够对流执行操作,因此我已导入Data.Foldable并在其上定义了流:

import Data.Foldable

instance Foldable Stream where
  foldr k z (Stream sf s) = go (sf s)
    where
      go Nothing = z
      go (Just (e, ns)) = e `k` go (sf ns)

为了测试我的流的速度,我已经定义了以下功能:

mysum = foldl' (+) 0

现在我们可以比较普通列表的速度和我的流类型:

x1 = [1..n]
x2 = Stream (\s -> if (s == n + 1) then Nothing else Just (s, s + 1)) 1

--main = print $ mysum x1
--main = print $ mysum x2

我的流速度大约是列表速度的一半(完整代码here)。

此外,这是一个最好的情况,没有列表或流:

bestcase :: Int
bestcase = go 1 0 where
  go i c = if i == n then c + i else go (i+1) (c+i)

这比列表和流版本快得多。

所以我有两个问题:

  1. 如何让我的流版本至少与列表一样快。
  2. 如何让我的流版本接近bestcase
  3. 的速度

1 个答案:

答案 0 :(得分:4)

根据您给出的折叠器定义foldl'来自Foldable的{​​{1}}。默认的实现是辉煌的,令人惊讶的好

foldl' :: (b -> a -> b) -> b -> t a -> b
foldl' f z0 xs = foldr f' id xs z0
  where f' x k z = k $! f z x

但是foldl'是你的特色;幸运的是,Foldable类包含foldl'作为方法,因此您只需将其添加到您的实例即可。

 foldl' op acc0 (Stream sf s0) = loop s0 acc0
   where 
    loop !s !acc = case sf s  of 
      Nothing -> acc
      Just (a,s') -> loop s' (op acc a)

对我而言,这似乎与bestcase几乎相同 请注意,这是一个标准情况,我们需要在累加器上使用严格注释。对于某些想法,您可以查看vector包对类似类型https://hackage.haskell.org/package/vector-0.10.12.2/docs/src/Data-Vector-Fusion-Stream.html的处理;或者在文本库https://github.com/bos/text/blob/master/Data/Text/Internal/Fusion的隐藏“融合”模块中。