[GHC] #14610: newtype wrapping of a monadic stack kills performance
GHC
ghc-devs at haskell.org
Tue Dec 26 05:29:35 UTC 2017
#14610: newtype wrapping of a monadic stack kills performance
-------------------------------------+-------------------------------------
Reporter: mrkkrp | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone: 8.6.1
Component: Compiler | Version: 8.2.2
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: Runtime | Unknown/Multiple
performance bug | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by dfeuer):
Here's an example that doesn't quite go far enough to demonstrate the
problem, but seems closer in spirit to the original.
{{{#!hs
newtype D a = D {getD :: a}
d :: a -> D a
d a = D a
baz :: D Int -> Int -> D Int
baz y x0 = foo x0
where
foo :: Int -> D Int
foo 0 = y
foo x = D (bar (x - 3))
bar :: Int -> Int
bar 0 = getD y
bar x = getD (foo x)
}}}
Compiling with `-dverbose-core2core`, we see that after the first
simplifier run (gentle, before floating), we get
{{{
baz :: D Int -> Int -> D Int
[LclIdX,
Arity=2,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 0] 121 0}]
baz
= \ (y_aSE :: D Int) (x0_aSF :: Int) ->
letrec {
foo_aSG [Occ=LoopBreaker] :: Int -> D Int
[LclId,
Arity=1,
Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [20] 81
0}]
foo_aSG
= \ (ds_d27R :: Int) ->
case ds_d27R of { GHC.Types.I# ds_d27T ->
case ds_d27T of ds_X289 {
__DEFAULT ->
(case ds_X289 of ds_X284 {
__DEFAULT ->
(foo_aSG (GHC.Types.I# (GHC.Prim.-# ds_X284 3#)))
`cast` (Foo.N:D[0] <Int>_R :: (D Int :: *) ~R# (Int
:: *));
3# ->
y_aSE `cast` (Foo.N:D[0] <Int>_R :: (D Int :: *)
~R# (Int :: *))
})
`cast` (Sym (Foo.N:D[0] <Int>_R) :: (Int :: *) ~R# (D
Int :: *));
0# -> y_aSE
}
}; } in
foo_aSG x0_aSF
}}}
Note that `foo_aSG` is bound by `letrec`.
If we switch to a type synonym version,
{{{#!hs
type D a = a
getD :: D a -> a
getD a = a
d :: a -> D a
d a = a
baz :: Int -> Int -> Int
baz y x0 = foo x0
where
foo :: Int -> Int
foo 0 = y
foo x = d (bar (x - 3))
bar :: Int -> Int
bar 0 = getD y
bar x = getD (foo x)
}}}
then at the same point in core2core we instead see
{{{#!hs
baz :: Int -> Int -> Int
[LclIdX,
Arity=2,
Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 0] 59 0}]
baz
= \ (y_aSQ :: Int) (x0_aSR :: Int) ->
joinrec {
foo_aSS [Occ=LoopBreaker] :: Int -> Int
[LclId[JoinId(1)],
Arity=1,
Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [20] 55
0}]
foo_aSS (ds_d27X :: Int)
= case ds_d27X of { GHC.Types.I# ds_d27Z ->
case ds_d27Z of ds_X28a {
__DEFAULT -> jump foo_aSS (GHC.Types.I# (GHC.Prim.-# ds_X28a
3#));
0# -> y_aSQ;
3# -> y_aSQ
}
}; } in
jump foo_aSS x0_aSR
}}}
The reason this example doesn't quite go far enough is that later
transformations work out the kinks and recognize the join point. But based
on the bug report, that isn't always the case.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14610#comment:5>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list