To add third case: 1. which optimising GHC passes does SPECIALIZE enable? Taking the example from the GHC docs https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/pragmas.html... : hammeredLookup :: Ord key => [(key, value)] -> key -> value {-# SPECIALIZE hammeredLookup :: [(Widget, value)] -> Widget -> value #-} This means that the type class dictionary for Ord will not be passed as a (hidden) argument in Core to `hammeredLookup` when Widget is the key. The GHC docs about overloading https://wiki.haskell.org/Performance/Overloading says "The presence of overloading can prevent many other optimisations, such as Strictness, from kicking in". So we lose the strictness GHC optimiser in the absence of SPECIALIZE rules on overloaded functions. Other than strictness analysis, what other GHC optimisations are being missed when overloading is not dealt with using monomorphism? And what are the specific runtime overheads of passing type class constraints as dictionaries to overloaded functions at runtime? -- Rob On 9 October 2015 at 12:30, Rob Stewart <robstewart57@gmail.com> wrote:
Hi,
I'm in search of clarifications and descriptions about code optimisations that are recommended in GHC docs, regarding inlining and newtype vs data.
1. Inlining small and often used functions
See https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/pragmas.html , Section "7.22.6.1. INLINE pragma". It says:
"GHC tries to inline functions/values that are 'small enough', thus avoiding the call overhead and possibly exposing other more-wonderful optimisations."
I understand the reason for function inlining with other languages like C, as it avoids the construction of new stack frames and coordinating stack pointers for each function call. In Haskell, rather than call stack we've got evaluation stacks.
Here are my questions: what exactly is the call overhead of a non-inlned Haskell function? What additional runtime work needs to happen when calling to a non-inlined function? Specifically what other GHC optimisations are possible only if function inlining happens first? Can anyone provide a simple example where 1) a function is not inlined, versus 2) when that function is inlined, which demonstrates two things: 1) a GHC report of the additional optimisations that were applied because of inlining and 2) shorter program runtime as a result of inlining.
2. Calculating the memory costs of a data type
As Don Stewart nicely explains on SO http://stackoverflow.com/a/5889784/1526266 , we should always use newtype when we have only a single constructor which has a single field. His example is this:
data Book = Book Int Int newtype Book = Book (Int, Int)
With newtype, the `Book` construct is guaranteed to be erased at compile time, to have the same representation as `(Int,Int)`. How to I measure the memory space costs of having `Book Int Int` hanging around at runtime, as opposed to just `(Int,Int)`? How many bytes does it cost to store `Book 4 5`? How many bytes does it cost to store `(4,5)` ? Can I ask GHC to tell me how many bytes the storage of each object would require? Or more generally, would looking at GHC Core for all my data structures inform me of the memory space costs for each of them?
Thanks
-- Rob