Data.Vector.modify在每次迭代时创建矢量副本

时间:2014-01-15 13:04:35

标签: haskell optimization vector

考虑一下这个例子:

import qualified Data.Vector.Unboxed as Vector
import Data.Vector.Unboxed (Vector)
import qualified Data.Vector.Unboxed.Mutable as MVector

process :: [Int] -> Vector Int -> Vector Int
process [] v = v
process (x:xs) v = process xs $ Vector.modify modify v
  where
  modify mv = do
    old <- MVector.read mv x
    MVector.write mv x (old + 1)

main :: IO ()
main = do
  print $ process [1, 1, 3, 1] $ Vector.replicate 10 0

在每次迭代的core中,我会看到newByteArray#copyByteArray#readIntArray#writeIntArray#unsafeFreezeByteArray#的序列。它肯定会创建中间副本。

文档说明:

  

对矢量应用破坏性操作。操作将是   如果可以安全地执行并且将修改副本   否则就是载体。

实现为

modify p = new . New.modify p . clone

还有RULE

"clone/new [Vector]" forall p.
  clone (new p) = p

从我(非常有限)的理解,连续两个modify不应该创建中间副本:

modify p2 . modify p1
=> new . New.modify p2 . clone . new . New.modify p1 . clone
=> new . New.modify p2 . New.modify p1 . clone

为什么它在process函数中不起作用?我认为应该:

process [1, 2] v
=> process [2] $ Vector.modify modify v
=> process [] $ Vector.modify modify $ Vector.modify modify v
=> Vector.modify modify $ Vector.modify modify v
=> the same as above

我怎样才能让它发挥作用?

2 个答案:

答案 0 :(得分:5)

我认为这是阻止它的递归。

源代码中没有任何地方显示modify后紧跟modify。函数的每次迭代再次调用modify,但这发生在运行时,而不是编译时。我认为这就是问题所在。如果您要在一行中手动编写三个modify个电话,它们就会融合。

我无法想到一种自动融合的方法。您当然可以自己手动调用clonenew,但我想不出让编译器为您执行此操作的方法。

答案 1 :(得分:0)

为什么不直接拨打modify

import Control.Monad (forM_)

[...]

process :: [Int] -> Vector Int -> Vector Int
process xs v = Vector.modify modify v
  where
  modify mv = forM_ xs $ \ x -> do
    old <- MVector.read mv x
    MVector.write mv x (old + 1)