在unboxed vector中,不会与unsafeUpdate_进行流合并 [英] No stream fusion with unsafeUpdate_ in unboxed vector
问题描述
如果使用 unsafeUpdate _
函数来更新向量
中的某些元素,是否有可能维持流融合一个矢量
?答案似乎没有在我做的测试中。对于下面的代码,在 upd
函数中生成了临时向量,如核心所示:
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]
在核心中, $ wupd
函数用于 sum
- 如下所示,它会生成新的 bytearray
:
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` ...
在 sum
函数的核心中有一个很好的紧密循环,但是在循环之前,有一个调用 $ wupd
函数,所以是临时生成。
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.
有没有办法避免在这个例子中的临时生成?我想到的方式是,在索引i中更新矢量是解析一个流,但只对索引i中的流进行操作(跳过其余部分),并用另一个元素替换那里的元素所以,更新任意位置的矢量不应该破坏流合并,对?
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?
推荐答案
我不能100%确定,因为使用 vector
它是一直下降的龟(你永远不会真正达到实际的实现,总是有另一个间接寻址),但是我明白了,更新
变种通过克隆强制一个新的临时:
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, the update
variants force a new temporary through cloning:
unsafeUpdate_ :: (Vector v a, Vector v Int) => v a -> v Int -> v a -> v a
{-# INLINE unsafeUpdate_ #-}
unsafeUpdate_ v is w
= unsafeUpdate_stream v (Stream.zipWith (,) (stream is) (stream w))
unsafeUpdate_stream :: Vector v a => v a -> Stream (Int,a) -> v a
{-# INLINE unsafeUpdate_stream #-}
unsafeUpdate_stream = modifyWithStream M.unsafeUpdate
和 modifyWithStream
调用 clone
(和 new
),
modifyWithStream :: Vector v a
=> (forall s. Mutable v s a -> Stream b -> ST s ())
-> v a -> Stream b -> v a
{-# INLINE modifyWithStream #-}
modifyWithStream p v s = new (New.modifyWithStream p (clone v) s)
new :: Vector v a => New v a -> v a
{-# INLINE_STREAM new #-}
new m = m `seq` runST (unsafeFreeze =<< New.run m)
-- | Convert a vector to an initialiser which, when run, produces a copy of
-- the vector.
clone :: Vector v a => v a -> New v a
{-# INLINE_STREAM clone #-}
clone v = v `seq` New.create (
do
mv <- M.new (length v)
unsafeCopy mv v
return mv)
我看不出<$ code> vector 会再次摆脱 unsafeCopy
。
这篇关于在unboxed vector中,不会与unsafeUpdate_进行流合并的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!