为什么 `Vector.length (Vector.replicate n 0)" 没有融合?
Why `Vector.length (Vector.replicate n 0)" is not fused?
以下代码意外地(至少对我而言)产生了一个中间向量:
import qualified Data.Vector as Vector
main :: IO ()
main =
print (test n)
n :: Int
n = 1000000
test :: Int -> Int
test n = Vector.length (Vector.replicate n (0 :: Int))
Core 的相关部分在这里(注意 newArray# 1000000
调用):
Main.main4
:: forall s_a38t.
GHC.Prim.State# s_a38t
-> (# GHC.Prim.State# s_a38t, Vector.Vector Int #)
[GblId,
Arity=1,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [0] 399 30}]
Main.main4 =
\ (@ s_a38t) (s1_a38u [OS=OneShot] :: GHC.Prim.State# s_a38t) ->
case GHC.Prim.newArray#
@ Int
@ (Control.Monad.Primitive.PrimState (GHC.ST.ST s_a38t))
1000000
(Data.Vector.Mutable.uninitialised @ Int)
(s1_a38u
`cast` ((GHC.Prim.State#
(Sym (Control.Monad.Primitive.TFCo:R:PrimStateST[0] <s_a38t>_N)))_R
:: GHC.Prim.State# s_a38t
~R# GHC.Prim.State#
(Control.Monad.Primitive.PrimState (GHC.ST.ST s_a38t))))
of _ [Occ=Dead] { (# ipv_a5RG, ipv1_a5RH #) ->
letrec {
$wa_s609 [InlPrag=[0], Occ=LoopBreaker]
:: GHC.Types.SPEC
-> GHC.Prim.Int#
-> Bool
-> GHC.Prim.State# s_a38t
-> (# GHC.Prim.State# s_a38t, Int #)
[LclId, Arity=4, Str=DmdType <S,1*U><L,U><S,1*U><L,U>]
$wa_s609 =
...
同时如果我将length
替换为sum
,融合就会正确发生:
test n = Vector.sum (Vector.replicate n (0 :: Int))
核心:
Rec {
Main.main_$s$wfoldlM'_loop [Occ=LoopBreaker]
:: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int#
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType <L,U><L,U>]
Main.main_$s$wfoldlM'_loop =
\ (sc_s6bx :: GHC.Prim.Int#) (sc1_s6by :: GHC.Prim.Int#) ->
case GHC.Prim.tagToEnum# @ Bool (GHC.Prim.<=# sc1_s6by 0)
of _ [Occ=Dead] {
False ->
Main.main_$s$wfoldlM'_loop sc_s6bx (GHC.Prim.-# sc1_s6by 1);
True -> sc_s6bx
}
end Rec }
Main.main2 :: String
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 100 30}]
Main.main2 =
case Main.main_$s$wfoldlM'_loop 0 1000000 of ww_s67W { __DEFAULT ->
case GHC.Show.$wshowSignedInt 0 ww_s67W (GHC.Types.[] @ Char)
of _ [Occ=Dead] { (# ww5_a5Vq, ww6_a5Vr #) ->
GHC.Types.: @ Char ww5_a5Vq ww6_a5Vr
}
}
此外,如果我根据 monadic 流组合器重写原始函数,中间向量也不会分配:
import qualified Data.Vector.Fusion.Stream.Monadic as Stream
import Data.Functor.Identity
test n = runIdentity $ Stream.length (Stream.replicate n (0 :: Int))
核心:
Rec {
Main.main_$s$wfoldlM'_loop [Occ=LoopBreaker]
:: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int#
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType <L,U><L,U>]
Main.main_$s$wfoldlM'_loop =
\ (sc_s5lE :: GHC.Prim.Int#) (sc1_s5lF :: GHC.Prim.Int#) ->
case GHC.Prim.tagToEnum# @ Bool (GHC.Prim.<=# sc1_s5lF 0)
of _ [Occ=Dead] {
False ->
Main.main_$s$wfoldlM'_loop
(GHC.Prim.+# sc_s5lE 1) (GHC.Prim.-# sc1_s5lF 1);
True -> sc_s5lE
}
end Rec }
Main.main2 :: String
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 100 30}]
Main.main2 =
case Main.main_$s$wfoldlM'_loop 0 1000000 of ww_s5ke { __DEFAULT ->
case GHC.Show.$wshowSignedInt 0 ww_s5ke (GHC.Types.[] @ Char)
of _ [Occ=Dead] { (# ww5_a5gi, ww6_a5gj #) ->
GHC.Types.: @ Char ww5_a5gi ww6_a5gj
}
}
为什么 Vector.length
会破坏融合?
我正在使用 ghc-7.10.3
和 vector-0.11.0.0
。
添加:
这是一个问题:https://github.com/haskell/vector/issues/111
我使用了 Data.Vector.Generic
中的 sum
和 length
而不是 Data.Vector
,因为后者只是被定义为前者。
这是长度的代码(来自 Data.Vector.Generic
)...
-- | /O(1)/ Yield the length of the vector.
length :: Vector v a => v a -> Int
{-# INLINE length #-}
length = Bundle.length . stream
嗯..让我们看看"sum"
-- | /O(n)/ Compute the sum of the elements
sum :: (Vector v a, Num a) => v a -> a
{-# INLINE sum #-}
sum = Bundle.foldl' (+) 0 . stream
但是如果我 运行 ghc -ddump-inlinings -ddump-rule-firings -O2
和我看到
Rule fired: SPEC Data.Vector.$fVectorVectora [GHC.Types.Int]
Inlining done: System.IO.print
Inlining done: System.IO.print1
Inlining done: Data.Vector.Generic.sum
Rule fired: Class op +
Rule fired: Class op fromInteger
Inlining done: GHC.Num.$fNumInt_$cfromInteger
Rule fired: integerToInt
Inlining done: Data.Vector.Fusion.Util.unId
Inlining done: Data.Vector.Fusion.Util.unId1
Inlining done: Data.Vector.replicate
Inlining done: Data.Vector.Generic.replicate
如果我 运行 它与 length
我看到:
Rule fired: SPEC Data.Vector.$fVectorVectora [GHC.Types.Int]
Inlining done: System.IO.print
Inlining done: System.IO.print1
Inlining done: Data.Vector.replicate
Inlining done: Data.Vector.Generic.replicate
Rule fired: SPEC Data.Vector.$fVectorVectora [GHC.Types.Int]
所以 sum
被内联而 length
没有,我不明白为什么。即使将展开阈值调高到荒谬的数量也不会改变这一点。
也就是说,如果我手动将 Vector.length
替换为 Bundle.length . Vector.stream
,stream/unstream
规则 会触发 ,如 sum
情况下,生成了一个非常整洁的核心,没有分配数组。
这是 sclv 答案的扩展。
我注意到 vector-0.11.0.0
出现了问题中的行为,但我碰巧安装的另一个版本 vector-0.10.12.2
却没有。使用 ghc --show-iface
检查这两个版本的 Data/Vector/Generic.hi
文件,我发现仅在版本 0.11.0.0
中,length
(但不是 sum
)被标记为 "loop-breaker"。这意味着 length
是相互递归定义组的一部分,GHC 选择此函数不内联以避免无限扩展的可能性。
我假设发生的事情是 0.11.0.0
中的更改使 length
成为定义循环的一部分,可能是无意的,以前不是,但我没有尝试验证因为它需要实际阅读 vector
源代码。
以下代码意外地(至少对我而言)产生了一个中间向量:
import qualified Data.Vector as Vector
main :: IO ()
main =
print (test n)
n :: Int
n = 1000000
test :: Int -> Int
test n = Vector.length (Vector.replicate n (0 :: Int))
Core 的相关部分在这里(注意 newArray# 1000000
调用):
Main.main4
:: forall s_a38t.
GHC.Prim.State# s_a38t
-> (# GHC.Prim.State# s_a38t, Vector.Vector Int #)
[GblId,
Arity=1,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [0] 399 30}]
Main.main4 =
\ (@ s_a38t) (s1_a38u [OS=OneShot] :: GHC.Prim.State# s_a38t) ->
case GHC.Prim.newArray#
@ Int
@ (Control.Monad.Primitive.PrimState (GHC.ST.ST s_a38t))
1000000
(Data.Vector.Mutable.uninitialised @ Int)
(s1_a38u
`cast` ((GHC.Prim.State#
(Sym (Control.Monad.Primitive.TFCo:R:PrimStateST[0] <s_a38t>_N)))_R
:: GHC.Prim.State# s_a38t
~R# GHC.Prim.State#
(Control.Monad.Primitive.PrimState (GHC.ST.ST s_a38t))))
of _ [Occ=Dead] { (# ipv_a5RG, ipv1_a5RH #) ->
letrec {
$wa_s609 [InlPrag=[0], Occ=LoopBreaker]
:: GHC.Types.SPEC
-> GHC.Prim.Int#
-> Bool
-> GHC.Prim.State# s_a38t
-> (# GHC.Prim.State# s_a38t, Int #)
[LclId, Arity=4, Str=DmdType <S,1*U><L,U><S,1*U><L,U>]
$wa_s609 =
...
同时如果我将length
替换为sum
,融合就会正确发生:
test n = Vector.sum (Vector.replicate n (0 :: Int))
核心:
Rec {
Main.main_$s$wfoldlM'_loop [Occ=LoopBreaker]
:: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int#
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType <L,U><L,U>]
Main.main_$s$wfoldlM'_loop =
\ (sc_s6bx :: GHC.Prim.Int#) (sc1_s6by :: GHC.Prim.Int#) ->
case GHC.Prim.tagToEnum# @ Bool (GHC.Prim.<=# sc1_s6by 0)
of _ [Occ=Dead] {
False ->
Main.main_$s$wfoldlM'_loop sc_s6bx (GHC.Prim.-# sc1_s6by 1);
True -> sc_s6bx
}
end Rec }
Main.main2 :: String
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 100 30}]
Main.main2 =
case Main.main_$s$wfoldlM'_loop 0 1000000 of ww_s67W { __DEFAULT ->
case GHC.Show.$wshowSignedInt 0 ww_s67W (GHC.Types.[] @ Char)
of _ [Occ=Dead] { (# ww5_a5Vq, ww6_a5Vr #) ->
GHC.Types.: @ Char ww5_a5Vq ww6_a5Vr
}
}
此外,如果我根据 monadic 流组合器重写原始函数,中间向量也不会分配:
import qualified Data.Vector.Fusion.Stream.Monadic as Stream
import Data.Functor.Identity
test n = runIdentity $ Stream.length (Stream.replicate n (0 :: Int))
核心:
Rec {
Main.main_$s$wfoldlM'_loop [Occ=LoopBreaker]
:: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int#
[GblId, Arity=2, Caf=NoCafRefs, Str=DmdType <L,U><L,U>]
Main.main_$s$wfoldlM'_loop =
\ (sc_s5lE :: GHC.Prim.Int#) (sc1_s5lF :: GHC.Prim.Int#) ->
case GHC.Prim.tagToEnum# @ Bool (GHC.Prim.<=# sc1_s5lF 0)
of _ [Occ=Dead] {
False ->
Main.main_$s$wfoldlM'_loop
(GHC.Prim.+# sc_s5lE 1) (GHC.Prim.-# sc1_s5lF 1);
True -> sc_s5lE
}
end Rec }
Main.main2 :: String
[GblId,
Str=DmdType,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 100 30}]
Main.main2 =
case Main.main_$s$wfoldlM'_loop 0 1000000 of ww_s5ke { __DEFAULT ->
case GHC.Show.$wshowSignedInt 0 ww_s5ke (GHC.Types.[] @ Char)
of _ [Occ=Dead] { (# ww5_a5gi, ww6_a5gj #) ->
GHC.Types.: @ Char ww5_a5gi ww6_a5gj
}
}
为什么 Vector.length
会破坏融合?
我正在使用 ghc-7.10.3
和 vector-0.11.0.0
。
添加: 这是一个问题:https://github.com/haskell/vector/issues/111
我使用了 Data.Vector.Generic
中的 sum
和 length
而不是 Data.Vector
,因为后者只是被定义为前者。
这是长度的代码(来自 Data.Vector.Generic
)...
-- | /O(1)/ Yield the length of the vector.
length :: Vector v a => v a -> Int
{-# INLINE length #-}
length = Bundle.length . stream
嗯..让我们看看"sum"
-- | /O(n)/ Compute the sum of the elements
sum :: (Vector v a, Num a) => v a -> a
{-# INLINE sum #-}
sum = Bundle.foldl' (+) 0 . stream
但是如果我 运行 ghc -ddump-inlinings -ddump-rule-firings -O2
和我看到
Rule fired: SPEC Data.Vector.$fVectorVectora [GHC.Types.Int]
Inlining done: System.IO.print
Inlining done: System.IO.print1
Inlining done: Data.Vector.Generic.sum
Rule fired: Class op +
Rule fired: Class op fromInteger
Inlining done: GHC.Num.$fNumInt_$cfromInteger
Rule fired: integerToInt
Inlining done: Data.Vector.Fusion.Util.unId
Inlining done: Data.Vector.Fusion.Util.unId1
Inlining done: Data.Vector.replicate
Inlining done: Data.Vector.Generic.replicate
如果我 运行 它与 length
我看到:
Rule fired: SPEC Data.Vector.$fVectorVectora [GHC.Types.Int]
Inlining done: System.IO.print
Inlining done: System.IO.print1
Inlining done: Data.Vector.replicate
Inlining done: Data.Vector.Generic.replicate
Rule fired: SPEC Data.Vector.$fVectorVectora [GHC.Types.Int]
所以 sum
被内联而 length
没有,我不明白为什么。即使将展开阈值调高到荒谬的数量也不会改变这一点。
也就是说,如果我手动将 Vector.length
替换为 Bundle.length . Vector.stream
,stream/unstream
规则 会触发 ,如 sum
情况下,生成了一个非常整洁的核心,没有分配数组。
这是 sclv 答案的扩展。
我注意到 vector-0.11.0.0
出现了问题中的行为,但我碰巧安装的另一个版本 vector-0.10.12.2
却没有。使用 ghc --show-iface
检查这两个版本的 Data/Vector/Generic.hi
文件,我发现仅在版本 0.11.0.0
中,length
(但不是 sum
)被标记为 "loop-breaker"。这意味着 length
是相互递归定义组的一部分,GHC 选择此函数不内联以避免无限扩展的可能性。
我假设发生的事情是 0.11.0.0
中的更改使 length
成为定义循环的一部分,可能是无意的,以前不是,但我没有尝试验证因为它需要实际阅读 vector
源代码。