[GHC] #8095: TypeFamilies painfully slow
GHC
ghc-devs at haskell.org
Tue Oct 6 12:10:07 UTC 2015
#8095: TypeFamilies painfully slow
-------------------------------------+-------------------------------------
Reporter: MikeIzbicki | Owner: bgamari
Type: bug | Status: new
Priority: high | Milestone: 8.0.1
Component: Compiler (Type | Version: 7.6.3
checker) |
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: Compile-time | Unknown/Multiple
performance bug | Test Case:
Blocked By: | Blocking:
Related Tickets: 5321 | Differential Rev(s):
-------------------------------------+-------------------------------------
Comment (by bgamari):
Finally have a chance to look at this. In particular I'll be looking at
the `b a` case identified in comment:6 as still being slow. For the record
`runghc Types.hs 5 b a` produces output of the form,
{{{#!hs
{-# LANGUAGE
TypeOperators,DataKinds,KindSignatures,TypeFamilies,PolyKinds,UndecidableInstances
#-}
import GHC.TypeLits
data Nat1 = Zero | Succ Nat1
type family Replicate1 (n :: Nat1) (x::a) :: [a]
type instance Replicate1 n x = Replicate1' '[] n x
type family Replicate1' (acc::[a]) (n :: Nat1) (x::a) :: [a]
type instance Replicate1' acc Zero x = acc
type instance Replicate1' acc (Succ n) x = Replicate1' (x ': acc) n x
class Class a where
f :: a -> a
data Data (xs::a) = X | Y
deriving (Read,Show)
main = print test1
-- The numeric parameter adjusts this
-- This definition is inlined in the actual output
type N = Succ (Succ (Succ (Succ (Succ Zero)))
instance (xs ~ Replicate1 N ()) => Class (Data xs) where
f X = Y
f Y = X
test1 = f (X :: Data (Replicate1 N () ))
}}}
In particular the numeric parameter to `Test` is adjusting `N`.
In the case of `N=500` compile-time is quite bad indeed,
{{{
$ ./Types 500 b a >| test.hs && time ghc test.hs -freduction-depth=0
-dshow-passes
Glasgow Haskell Compiler, Version 7.11.20151006, stage 2 booted by GHC
version 7.10.2
...
*** Chasing dependencies:
Chasing modules from: *test.hs
Stable obj: []
Stable BCO: []
Ready for upsweep
[NONREC
ModSummary {
ms_hs_date = 2015-10-06 11:44:53.905638216 UTC
ms_mod = Main,
ms_textual_imps = [import (implicit) Prelude, import
GHC.TypeLits]
ms_srcimps = []
}]
*** Deleting temp files:
compile: input file test.hs
Created temporary directory: /tmp/ghc27682_0
*** Checking old interface for Main:
[1 of 1] Compiling Main ( test.hs, test.o )
*** Parser:
*** Renamer/typechecker:
*** Desugar:
Result size of Desugar (after optimization)
= {terms: 103, types: 11,779, coercions: 509,036}
*** Simplifier:
Result size of Simplifier iteration=1
= {terms: 107, types: 7,294, coercions: 516}
Result size of Simplifier
= {terms: 107, types: 7,294, coercions: 516}
*** Tidy Core:
Result size of Tidy Core
= {terms: 111, types: 7,302, coercions: 518}
*** CorePrep:
Result size of CorePrep
= {terms: 152, types: 7,880, coercions: 518}
*** Stg2Stg:
*** CodeGen:
*** Assembler:
Upsweep completely successful.
*** Deleting temp files:
Warning: deleting non-existent /tmp/ghc27682_0/ghc_3.c
Warning: deleting non-existent /tmp/ghc27682_0/ghc_1.s
Linking test ...
*** C Compiler:
*** C Compiler:
*** Linker:
*** Deleting temp files:
*** Deleting temp dirs:
real 0m22.835s
user 0m22.514s
sys 0m0.315s
}}}
It appears that the desugaring pass ends up producing a number of
coercions quadratic in the number of depth of the equality in the context
result (with almost all being eliminated during simplification),
||= input =||= terms =||= types =||= coercions =||
|| 400 b a || 103 || 9479 || 327236 ||
|| 200 b a || 103 || 4879 || 83636 ||
|| 100 b a || 103 || 2579 || 21836 ||
|| 50 b a || 103 || 1429 || 5936 ||
|| 2 b a || 103 || 325 || 80 ||
Looking at the output from desugaring, it seems that the only definition
which is growing non-linearly is `main` itself, where indeed we find a
chain of quadratically growing coercions. For instance, in the case of
`N=3` we have,
{{{#!hs
-- RHS size: {terms: 6, types: 85, coercions: 102}
main :: IO ()
[LclIdX, Str=DmdType]
main =
print
@ (Data '[(), (), ()])
(Main.$fShowData @ [*] @ '[(), (), ()])
(f @ (Data '[(), (), ()])
(Main.$fClassData
@ '[(), (), ()]
(Eq#
@ [*]
@ '[(), (), ()]
@ (Replicate1 ('Succ ('Succ ('Succ 'Zero))) ())
@~ (Sym
(Main.TFCo:R:Replicate1'kaccZerox[0]
<*>_N <'[(), (), ()]>_N <()>_N)
; Sym
(Main.TFCo:R:Replicate1'kaccSuccx[0]
<*>_N <'[(), ()]>_N <'Zero>_N <()>_N)
; Sym
(Main.TFCo:R:Replicate1'kaccSuccx[0]
<*>_N <'[()]>_N <'Succ 'Zero>_N <()>_N)
; Sym
(Main.TFCo:R:Replicate1'kaccSuccx[0]
<*>_N <'[]>_N <'Succ ('Succ 'Zero)>_N <()>_N)
; Sym
(Main.TFCo:R:Replicate1knx[0]
<*>_N <'Succ ('Succ ('Succ 'Zero))>_N <()>_N)
:: '[(), (), ()] ~# Replicate1 ('Succ ('Succ ('Succ
'Zero))) ())))
(((Main.X @ [*] @ '[(), (), ()])
`cast` ((Data
(UnivCo opt_phantom phantom
'[(), (), ()] (Replicate1 ('Succ ('Succ ('Succ
'Zero))) ())))_R
:: Data '[(), (), ()]
~R# Data (Replicate1 ('Succ ('Succ ('Succ 'Zero)))
())))
`cast` ((Data
(UnivCo opt_phantom phantom
(Replicate1 ('Succ ('Succ ('Succ 'Zero))) ()) '[(),
(), ()]))_R
:: Data (Replicate1 ('Succ ('Succ ('Succ 'Zero))) ())
~R# Data '[(), (), ()])))
}}}
More analysis to follow.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8095#comment:10>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list