[GHC] #13966: Skip-less stream fusion
GHC
ghc-devs at haskell.org
Fri Jul 14 20:07:10 UTC 2017
#13966: Skip-less stream fusion
-------------------------------------+-------------------------------------
Reporter: jmspiewak | Owner: (none)
Type: feature request | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 8.2.1-rc3
Resolution: | Keywords: JoinPoints
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by jmspiewak):
A typeclass-based Skip-less stream (also from the blog post) does fuse.
{{{#!hs
data Step3 s = Done3 | Yield3 s (Item3 s)
class Stream3 s where
type Item3 s
next3 :: s -> Step3 s
data EnumFromTo3 a = EnumFromTo3 a a
instance (Ord a, Num a) => Stream3 (EnumFromTo3 a) where
type Item3 (EnumFromTo3 a) = a
next3 (EnumFromTo3 i high)
| i > high = Done3
| otherwise = Yield3 (EnumFromTo3 (i + 1) high) i
data Filter3 a s = Filter3 (a -> Bool) s
instance (Stream3 s, Item3 s ~ a) => Stream3 (Filter3 a s) where
type Item3 (Filter3 a s) = a
next3 (Filter3 predicate s0) = loop s0 where
loop s1 = case next3 s1 of
Done3 -> Done3
Yield3 s2 x
| predicate x -> Yield3 (Filter3 predicate s2) x
| otherwise -> loop s2
sum3 :: (Num (Item3 s), Stream3 s) => s -> Item3 s
sum3 = loop 0 where
loop total s1 = case next3 s1 of
Done3 -> total
Yield3 s2 x -> loop (total + x) s2
{-# NOINLINE chain3 #-}
chain3 :: Int -> Int
chain3 = sum3 . Filter3 even . EnumFromTo3 1
}}}
Adding an existential wrapper doesn't break the fusion.
{{{#!hs
data Stream4 a = forall s. (Stream3 s, Item3 s ~ a) => Stream4 s
enumFromTo4 :: (Ord a, Num a) => a -> a -> Stream4 a
enumFromTo4 start high = Stream4 (EnumFromTo3 start high)
filter4 :: (a -> Bool) -> Stream4 a -> Stream4 a
filter4 p (Stream4 s) = Stream4 (Filter3 p s)
sum4 :: Num a => Stream4 a -> a
sum4 (Stream4 s) = sum3 s
{-# NOINLINE chain4 #-}
chain4 :: Int -> Int
chain4 = sum4 . filter4 even . enumFromTo4 1
}}}
{{{
benchmarking typeclass Skip-less
time 73.11 ms (72.50 ms .. 73.94 ms)
0.999 R² (0.998 R² .. 1.000 R²)
mean 69.80 ms (68.86 ms .. 70.72 ms)
std dev 2.916 ms (2.483 ms .. 3.577 ms)
variance introduced by outliers: 20% (moderately inflated)
benchmarking typeclass existential Skip-less
time 75.44 ms (74.91 ms .. 76.13 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 75.44 ms (75.06 ms .. 75.80 ms)
std dev 1.118 ms (904.6 μs .. 1.479 ms)
}}}
Both result in:
{{{
Rec {
-- RHS size: {terms: 36, types: 11, coercions: 0, joins: 1/1}
Main.main_$s$wloop1 [Occ=LoopBreaker]
:: Int# -> Int# -> Int# -> Int#
[GblId, Arity=3, Caf=NoCafRefs, Str=<S,U><S,U><S,U>]
Main.main_$s$wloop1
= \ (sc_s9HL :: Int#) (sc1_s9HK :: Int#) (sc2_s9HJ :: Int#) ->
joinrec {
$wloop2_s9y9 [InlPrag=[0], Occ=LoopBreaker] :: Int# -> Int# ->
Int#
[LclId[JoinId(2)], Arity=2, Str=<S,U><S,U>, Unf=OtherCon []]
$wloop2_s9y9 (ww_s9y1 :: Int#) (ww1_s9y6 :: Int#)
= case tagToEnum# @ Bool (># ww_s9y1 ww1_s9y6) of {
False ->
case remInt# ww_s9y1 2# of {
__DEFAULT -> jump $wloop2_s9y9 (+# ww_s9y1 1#) ww1_s9y6;
0# ->
Main.main_$s$wloop1 ww1_s9y6 (+# ww_s9y1 1#) (+#
sc2_s9HJ ww_s9y1)
};
True -> sc2_s9HJ
}; } in
jump $wloop2_s9y9 sc1_s9HK sc_s9HL
end Rec }
}}}
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13966#comment:2>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list