Writing Fast Haskell
Writing Fast Haskell
1
Introduction
1
Introduction
1
Introduction
1
Goals for Today
2
Benchmarking/Profiling Disclaimer
3
Primitive, Unlifted and Boxed Types
Primitive Types
Correspond to “raw machine types”
E.g. Int#, Double#
4
Primitive, Unlifted and Boxed Types
Primitive Types
Correspond to “raw machine types”
E.g. Int#, Double#
Boxed Types
Represented by a pointer to a heap object
E.g. all user-defined types, Int
4
Primitive, Unlifted and Boxed Types
Primitive Types
Correspond to “raw machine types”
E.g. Int#, Double#
Boxed Types
Represented by a pointer to a heap object
E.g. all user-defined types, Int
Unlifted Types
Cannot be bottom
E.g. all primitive types but also Array# (which is not
primitive)
4
Definition of the Int type
Int
I# Int#
5
GHC Compilation Pipeline
Haskell
Core
STG
C--
Assembly LLVM IR
6
GHC Compilation Pipeline
Core
STG
C--
Assembly LLVM IR
6
GHC Compilation Pipeline
Core Simplifier
STG
C--
Assembly LLVM IR
6
GHC Compilation Pipeline
Core Simplifier
C--
Assembly LLVM IR
6
Core’s Expr Type
data Expr b
= Var Id
| Lit Literal
| App (Expr b) (Arg b)
| Lam b (Expr b)
| Let (Bind b) (Expr b)
| Case (Expr b) b Type [Alt b]
| Cast (Expr b) Coercion
| Tick (Tickish Id) (Expr b)
| Type Type
| Coercion Coercion
deriving Data
7
Viewing Core
• -ddump-simpl or -ddump-prep
• Suppress info that you don’t care about
• -dsuppress-idinfo
• -dsuppress-ticks
• -dsuppress-module-prefixes
• -dsuppress-all
• GHC plugin that outputs core as HTML
8
Mental Model for Core
let
Allocates a thunk on the heap
case
Forces evaluation to WHNF
9
Naive Sum
10
Tail-Recursive Sum
11
Force the Accumulator
12
BangPatterns
13
WHNF
14
Strictness Annotations in Data Types
15
Avoiding Space Leaks
Rule of Thumb
Constant-size (e.g. Int) accumulators are often problematic
16
Specialization and Inlining
Specialization
Inlining
17
Cross-Module Specialization and Inlining
18
Specialization
19
Inlining
20
Inlining
20
Inlining
f a b = …
f = \a b …
21
Inlining
Or are they?
f a b = …
f = \a b …
21
Call Arity
21
Controlling Memory Layout
data Point =
Point Int
Int
Point
Int Int
I# Int# I# Int#
22
Controlling Memory Layout
data Point =
Point {-# UNPACK #-} !Int
{-# UNPACK #-} !Int
Point
23
Automatic Unpacking
24
Automatic Unpacking
24
Continuation Passing Style
main :: IO ()
main =
case loop2 100 (10, 10) of
(x, y) -> print (x - y)
25
Continuation Passing Style
Convert
f :: a -> b
into
26
Continuation Passing Style
main :: IO ()
main =
loop2 100 (10, 10) $ \(x, y) -> print (x - y)
27
Writing Your Own Optimizations: Rewrite Rules
map Fusion
28
Writing Your Own Optimizations: Rewrite Rules
map Fusion
build/foldr Fusion
build
:: (forall b. (a -> b -> b) -> b -> b)
-> [a]
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f a (build g) = g f a
28
Writing your Own Optimizations: Rewrite Rules
Example
{-# RULES
"map/map"
forall f g xs. map f (map g xs) =
map (f.g) xs
#-}
29
Array Primitives in GHC
Array#
SmallArray#
ByteArray#
30
High-Level Array Libraries
31
Basic Data Structures
32
Conclusion
33
Conclusion
33
More Information
34