210 likes | 375 Views
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. This Talk. Technique has been used by compiler writers for many years, e.g. GHC since 1991;
E N D
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
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.
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)
The Problem Suppose we wish to change the type of a recursive program, defined by prog = fix body. B A Type of the desired worker. Type of the original program.
Assumptions We assume conversion functions unwrap A B wrap such that: A can be faithfully represented by B. wrap . unwrap = idA
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 wrap work = Recursive program of type A. Wrapper of type B A. Recursive worker of type B.
The Final Step We simplify work = fix (unwrap . body . wrap) to eliminate the overhead of repeatedly converting between the two types, by fusing together and unwrap wrap
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.
Example - Reverse How can we improve: Quadratic time. rev [] = [] rev (x:xs) = rev xs ++ [x] Step 1 - express the program using fix rev = fix body body f [] = [] body f (x:xs) = f xs ++ [x]
Step 2 - choose a new type for the program rep [a] [a] [a] Key idea (Hughes): represent the result list as a function. where abs rep xs = (xs ++) abs f = f []
Step 3 – define conversion functions unwrap [a] [a] [a] [a] [a] wrap where Satisfies the worker/wrapper assumption. unwrap f = rep . f wrap g = abs . g
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.
Using properties of rep and Worker/wrapper fusion property. unwrap (wrap work) = work we obtain a linear time worker: work :: [a] [a] [a] work [] ys = ys work (x:xs) ys = work xs (x:ys)
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.
Example - Unboxing Int Int Int♯ Int♯ More efficient worker that uses unboxed integers. Type of a simple factorial function. Note: this is how GHC uses worker/wrapper.
Example - Memoisation Nat Nat Stream Nat More efficient worker that uses a memo table. Type of a simple Fibonacci function.
Example - Continuations Expr (Int Mint) Mint Mint Expr Mint More efficient worker that uses success and failure continuations. Type of a simple evaluation function that may fail, where Mint = Maybe Int.
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.
Further Work • Mechanising the technique; • Specialised patterns of recursion; • Generalisation using category theory; • Programs with effects; • Other application areas.