Let's take this file (from haskell-src-exts) and build it with -O0. On my machine it takes 55s.
If we remove deriving of all classes except Functor (which is the only one used by the module itself), the time will drop down to 4s.
Different classes vary in their compile-time cost, but there's no single culprit. Among the costly ones are Generic, Data, Show, Ord.
haskell-src-exts users are very annoyed by its long compile time, about 10 minutes, especially because we don't compile with -O0 by default. It would be nice to get this fixed.
Trac metadata
Trac field
Value
Version
7.8.3
Type
Bug
TypeOfFailure
OtherFailure
Priority
normal
Resolution
Unresolved
Component
Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
Deriving a lot of instances for a lot of large types generates a lot of code. Language.Haskell.Exts.Annotated.Syntax desugared to a program with 126,396 terms and took 24.2 seconds to build (with -O0). By comparison, the longest (in terms of lines, roughly 2100) module Language.Haskell.Exts.Annotated.ExactPrint desugared to a program with only 12,181 terms and it took 2.7 seconds to build. So, I don't think there is anything wrong with the speed of deriving instances specifically.
Now, it's possible that instance deriving could be made to generate smaller code than it currently does for some particular classes... but I didn't notice anything amiss scanning through the generated instances for Decl (with -dsuppress-all -ddump-deriv -ddump-to-file). So, I don't really think deriving is to blame here.
It undoubtedly is a bit unexpected if simply adding deriving( Show ) or whatever, which seems such a little thing, generates seriously large amounts of object code.
If someone feels able to investigate I would suggest:
Find out if any particular class is to blame. How much of the code is generated by which classes?
Study the generated code to see if it could be abstracted, so that instead of lots of code, there were calls to some suitable shared functions.
Obviously there is a performance/code-size issue to worry about, but my guess is that while Eq and Ord might be performance-critical, Read and Show are not.
But it would require some thoughtful investigation.
Study the generated code to see if it could be abstracted, so that instead of lots of code, there were calls to some suitable shared functions.
Looking at the Ord code (which seems to be the largest):
It seems that it could be made much smaller using <> (which is EQ <> x = x and y <> _ = y otherwise) instead of nested case expressions. I guess there would be a performance hit, though.
Do we really have to provide separate definitions for compare and < and <= and > and >=? Maybe using the default implementation for all methods but compare is good enough, and could bring code size down considerably.
Overreaching spontaneous idea: Add a method generalCompare :: r -> r -> r -> a -> a -> r to the Ord class. Implement that in deriving clauses, and have the default implementations use that. (e.g. (<) = generalCompare True False False). Should be faster than using compare + pattern matching. OTOH. all constructors of Comparing are static values, so there is probably not much to win here.
Obviously, people have considered my second suggest before, see Note [Do not rely on compare] in [source:ghc/compiler/typecheck/TcGenDeriv.lhs#L300]...
Is there an easy way to check what exactly takes up the time: generating the ast, renaming/typechecking it, generating the output code? Or do I have to compile ghc with profiling for that?