Powerpoint slides

THE WORKER / WRAPPER
TRANSFORMATION
Graham Hutton and Andy Gill
What Is It?
A technique for changing the type of a program in
order to improve its performance:
wrapper
program
worker
1
This Talk
Technique has been used by compiler writers for
many years, e.g. GHC since 1991;
But is little known in the wider community, and
has never been described precisely;
We explain, formalise and explore the generality
of the worker/wrapper transformation.
2
Fixed Points
The key to formalising the technique is the use of
explicit fixed points. For example:
ones = 1 : ones
can be rewritten as:
ones
= fix body
body xs = 1 : xs
fix f = f (fix f)
3
The Problem
Suppose we wish to change the type of a recursive
program, defined by prog = fix body.
A
Type of the
original program.
B
Type of the
desired worker.
4
Assumptions
We assume conversion functions
unwrap
A
B
wrap
such that:
wrap . unwrap = idA
A can be faithfully
represented by B.
5
Let’s Calculate!
prog
=
fix body
=
=
fix (idA . body)
Rolling rule.
fix (wrap . unwrap . body)
=
wrap (fix (unwrap . body . wrap))
=
wrap work
6
Summary
We have derived the following factorisation:
prog
Recursive
program of
type A.
=
wrap
Wrapper of
type B  A.
work
Recursive
worker of
type B.
7
The Final Step
We simplify
work = fix (unwrap . body . wrap)
to eliminate the overhead of repeatedly converting
between the two types, by fusing together
unwrap
and
wrap
8
The Worker / Wrapper Recipe
① Express the original program using fix;
② Choose the new type for the program;
③ Define appropriate conversion functions;
④ Apply the worker/wrapper transformation;
⑤ Simplify the resulting definitions.
9
Example - Reverse
How can we improve:
rev []
= []
rev (x:xs) = rev xs ++ [x]
Quadratic
time.
Step 1 - express the program using fix
rev
= fix body
body f []
= []
body f (x:xs) = f xs ++ [x]
10
Step 2 - choose a new type for the program
rep
[a]  [a]
[a]
abs
where
Key idea (Hughes):
represent the result
list as a function.
rep xs = (xs ++)
abs f
= f []
11
Step 3 – define conversion functions
unwrap
[a]  [a]
[a]  [a]  [a]
wrap
where
unwrap f = rep . f
wrap g
Satisfies the
worker/wrapper
assumption.
= abs . g
12
Step 4 – apply the transformation
rev
= wrap work
work = fix (unwrap . body . wrap)
Step 5 – simplify the result
rev
:: [a]  [a]
rev xs = work xs []
Expanding
out wrap.
13
Using properties of rep and
unwrap (wrap work)
=
work
Worker/wrapper
fusion property.
we obtain a linear time worker:
work
:: [a]  [a]  [a]
work []
ys = ys
work (x:xs) ys = work xs (x:ys)
14
Notes
Once the decision to use Hughes lists is made,
the derivation itself is straightforward;
No induction is required, other than the implicit
use to verify that lists form a monoid;
Fast reverse fits naturally into our paradigm, but
simpler derivations are of course possible.
15
Example - Unboxing
Int  Int
Type of a simple
factorial function.
Int♯  Int♯
More efficient
worker that uses
unboxed integers.
Note: this is how GHC uses worker/wrapper.
16
Example - Memoisation
Nat  Nat
Stream Nat
Type of a simple
Fibonacci function.
More efficient
worker that uses
a memo table.
17
Example - Continuations
Expr 
Expr  Mint
(Int  Mint) 
Mint  Mint
Type of a simple
evaluation function
that may fail, where
Mint = Maybe Int.
More efficient
worker that uses
success and failure
continuations.
18
Summary
General technique for changing the type of a
program to improve its performance;
Straightforward to understand/apply, requiring
only basic equational reasoning principles;
Captures many seemingly unrelated optimisation
methods in a single unified framework.
19
Further Work
Mechanising the technique;
Specialised patterns of recursion;
Generalisation using category theory;
Programs with effects;
Other application areas.
20