Performance/Overloading
Haskell Performance Resource
Constructs: Techniques: |
Overloaded functions are not your friend
Haskell's overloading (using type classes) is elegant, neat, etc., etc., but it is death to performance if left to linger in an inner loop. Each type class constraint on an overloaded function corresponds to an extra argument passed at runtime (called a dictionary) (except in Jhc!), and a reference to a class method is implemented by extracting a field from the dictionary. The presence of overloading can prevent many other optimisations, such as Strictness, from kicking in.
How can you squash overloading?
Give explicit type signatures: Signatures are the basic trick; putting them on exported, top-level functions is good software-engineering practice, anyway. (GHC Tip: using -fwarn-missing-signatures can help enforce good signature-practice).
The automatic specialisation of overloaded functions (with -O) should take care of overloaded local and/or unexported functions.
Use SPECIALIZE pragmas: Specialize the overloading on key functions in your program. (Supported by: GHC, Jhc).
Alternatively, use INLINE pragmas: This will just include function body at each call place. You can see sources of Library/Streams that heavily uses inlining to get highest performance marks even for polymorphism married to complex class structure. The back side of this heavy inlining is fast growth of compiled code. Don't use INLINE and SPECIALIZE on the same function. (Supported by: GHC, Jhc).
Use a module export list: If you omit the export list for the module, then everything in the module is exported. Also, any functions that lack type signatures will have the most general i.e. polymorphic types inferred for them. If those same functions are only used inside that module, then they are likely to be more polymorphic than necessary. Removing them from the module export list (whether explicit or implicit) will allow the compiler to specialise them to just the type(s) used in the module.