[GHC] #14941: Switching direct type family application to EqPred (~) prevents inlining in code using vector (10x slowdown)
GHC
ghc-devs at haskell.org
Tue Sep 18 22:30:47 UTC 2018
#14941: Switching direct type family application to EqPred (~) prevents inlining in
code using vector (10x slowdown)
-------------------------------------+-------------------------------------
Reporter: nh2 | Owner: davide
Type: bug | Status: new
Priority: normal | Milestone:
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 simonpj):
Thanks for the simpler example. I think I now understand what
is going on. It's all to do with strictness analysis.
Consider this function, and suppose it is strict in x:
{{{
f1 :: Int -> blah
f1 x = ...(case x of I# y -> ...)...
}}}
The strictness analysis sees that `f1` is strict in `x` and we get a w/w
split
{{{
f1 :: Int -> blah
f1 x = case x of I# y -> $wf y
$wf1 :: Int# -> blah
$wf1 y = let x = I# y in ...original body of f...
}}}
Now suppose that we have
{{{
type family F a
type instance F Bool = Int
f2 :: F Bool -> blah
f2 x = ...same as before...
}}}
In fact the strictness analysis still sees that `f2` is strict in `x`,
and worker wrapper works too -- all by using the family instances.
We get this
{{{
f2 :: F Bool -> blah
f2 x = case (x |> g1) of I# y -> $wf2 y
$wf2 :: Int# -> blah
$wf2 y = let x = (I# y) |> sym g
in ..as before...
}}}
Here `g` is a coercion `g :: F Bool ~ Int`, constructed from the family
instances. This coersionn is generated by `topNormaliseType_maybe`,
itself called from `deepSplitCprType_maybe` in `WwLib`, during
worker/wrapper
generation.
But it's harder if the coercion is purely local. Let's start with this
(yes I know you can't write `(~#)` in source Haskell but imagine this
is Core:
{{{
f3 :: forall a. (a ~# Int) => a -> blah
f3 a (g :: (a ~# Int) (x :: a) = ...as before...
}}}
What we'd like is this:
{{{
f3 :: forall a. (a ~# Int) => a -> blah
f3 a (g :: (a ~# Int) (x :: a) = case (x |> g) of I# y -> $wf3 a g y
$wf3 :: forall a. (a ~# Int) => Int# -> blah
$wf3 a g y = let x = (I# y) |> sym g
in ...as before...
}}}
but alas neither the demand analyser, nor the worker/wrapper generator
are clever enough to do this. We need a kind of `normaliseType` that
can take some local "givens" as well as the top-level family instance
envs.
This has the same ring as something Ryan wanted in the coverage
checker.
It's not obvious exactly what "should work". Eg what about `(Int ~# a)`,
or even `([Int] ~# [a])`?
Finally, there is the question of `(~)` vs `(~#)`. I think we are very
aggressive about unboxing `(~)` constraints, so I'd like this:
{{{
f4 :: forall a. (a ~ Int) => a -> blah
f4 a (g :: (a ~ Int) (x :: a)
= case g of MkTwiddle (g2 :: a ~# Int) ->
case (x |> g2) of I# y -> $wf4 a g2 y
$wf4 :: forall a. (a ~# Int) => Int# -> blah
$wf4 a g2 y = let x = (I# y) |> sym g2
g = MkTwiddle g2
in ...as before...
}}}
Making all this happen will take some work though.
How important is it. I'm tempted to say "don't write types like that"!
(Although the type checker goes to some trouble to support them well.)
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/14941#comment:9>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list