Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data.Vector.modify creates vector copies on each iteration

Consider the example:

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

In core on each iteration I see sequence of newByteArray#, copyByteArray#, readIntArray#, writeIntArray# and unsafeFreezeByteArray#. It definitely creates intermediate copies.

Documentation states:

Apply a destructive operation to a vector. The operation will be performed in place if it is safe to do so and will modify a copy of the vector otherwise.

It is implemented as

modify p = new . New.modify p . clone

And there is RULE

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

From my (pretty limited) understanding, two successive modify should not create intermediate copies:

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

Why doesn't it work in the process function? I think it should:

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

How can I make it work?

like image 559
Yuras Avatar asked Jan 15 '14 13:01

Yuras


1 Answers

I think it's the recursion that's stopping it.

There's nowhere in the source code that says modify immediately followed by modify. Each iteration of the function calls modify again, but that happens at run-time, not compile-time. I think that's the problem. If you were to manually write three modify calls in a line, they would fuse.

I can't think of a way to make this fuse automatically. You could of course manually call clone and new yourself, but I can't think of a way to get the compiler to do this for you.

like image 163
MathematicalOrchid Avatar answered Sep 22 '22 14:09

MathematicalOrchid