Is it possible to maintain stream fusion when processing a vector
if unsafeUpdate_
function is used to update some elements of a vector
? The answer seems to be no in the test I did. For the code below, temporary vector is generated in upd
function, as confirmed in the core:
module Main where
import Data.Vector.Unboxed as U
upd :: Vector Int -> Vector Int
upd v = U.unsafeUpdate_ v (U.fromList [0]) (U.fromList [2])
sum :: Vector Int -> Int
sum = U.sum . upd
main = print $ Main.sum $ U.fromList [1..3]
In the core, $wupd
function is used in sum
- as seen below, it generates new bytearray
:
$wupd :: Vector Int -> Vector Int
$wupd =
\ (w :: Vector Int) ->
case w `cast` ... of _ { Vector ipv ipv1 ipv2 ->
case main11 `cast` ... of _ { Vector ipv3 ipv4 ipv5 ->
case main7 `cast` ... of _ { Vector ipv6 ipv7 ipv8 ->
runSTRep
(\ (@ s) (s :: State# s) ->
case >=# ipv1 0 of _ {
False -> case main6 ipv1 of wild { };
True ->
case newByteArray# (*# ipv1 8) (s `cast` ...)
of _ { (# ipv9, ipv10 #) ->
case (copyByteArray# ipv2 (*# ipv 8) ipv10 0 (*# ipv1 8) ipv9)
`cast` ...
There is a nice, tight loop in the core for sum
function but just before that loop, there is a call to $wupd
function, and so, a temporary generation.
Is there a way to avoid temporary generation in the example here? The way I think about it, updating a vector in index i is the case of parsing a stream but only acting on the stream in index i (skipping the rest), and replacing the element there with another element. So, updating a vector in an arbitrary location shouldn't break stream fusion, right?
I can't be 100% sure, because with
vector
it's turtles all the way down (you never really reach the actual implementation, there's always another indirection), but as far as I understand it, theupdate
variants force a new temporary through cloning:and
modifyWithStream
callsclone
(andnew
),and I see no way that
vector
would get rid of thatunsafeCopy
again.If you need to change one or very few elements, there are nice solutions in
repa
andyarr
libraries. They preserve fusion (I'm not sure aboutrepa
) and Haskell-idiomatic.Repa, using
fromFunction
:Yarr, using
Delayed
: