+ All Categories
Home > Documents > Generating Efficient code

Generating Efficient code

Date post: 01-Feb-2016
Category:
Upload: alice
View: 35 times
Download: 0 times
Share this document with a friend
Description:
Generating Efficient code. Lecture 9. Assignment. Homework #6 is now available on the class web-site. Due next Tuesday, Feb 15, 2005 Read the paper ML Like Inference for Classifiers Assigned Thursday Feb 10, 2005 To be discussed, Thursday Feb 17, 2005 We will need a presenter …. - PowerPoint PPT Presentation
40
Generating Efficient code Lecture 9
Transcript
Page 1: Generating Efficient code

Generating Efficient code

Lecture 9

Page 2: Generating Efficient code

Assignment

• Homework #6 is now available on the class web-site.– Due next Tuesday, Feb 15, 2005

• Read the paper – ML Like Inference for Classifiers – Assigned Thursday Feb 10, 2005– To be discussed, Thursday Feb 17, 2005– We will need a presenter …

Page 3: Generating Efficient code

Generating Efficient Code

• Staged programs use known information to specialize complex algorithms.

• This often introduces constants and code duplication.

• Removing these inefficiencies after the fact is often difficult (if not impossible if code is abstract and cannot be taken apart).

• Good tools can help us remove the efficiencies as we generate the code.

Page 4: Generating Efficient code

The region language

datatype Region

= Univ

| Empty

| Circle of real

| Rect of real * real

| Union of Region*Region

| Inter of Region*Region

| Trans of ((real*real)*Region);

Page 5: Generating Efficient code

Example

val c = Circle 1.0;

fun layer [] = Empty | layer (x::xs) = Union(x,Trans((2.0,0.0),layer xs));

val r1 = Trans((~2.0,0.0),layer[c,c,c]);

Page 6: Generating Efficient code

The meaning of a region

• The meaning of a region is a set of points in the 2-D plane.

• Most regions have an infinite set of points

• We can capture this by using a characteristic function “P”

• P x true

• P y -> false • x

• y

Page 7: Generating Efficient code

Helper functions

fun plus x y = x+y;

fun minus x y = x-y;

fun neg x = 0.0 - x;

fun leq x y = x <= y;

fun square x = x*x;

fun between a b c =

(leq a b) andalso (leq b c);

Page 8: Generating Efficient code

The meaning functionfun mean Empty (x,y) = false | mean Univ (x,y) = true | mean (Circle r) (x,y) = (leq (plus (square x) (square y)) (square r)) | mean (Rect(w,h)) (x,y) = (between (neg w) (plus x x) w) andalso (between (neg h) (plus y y) h) | mean (Trans((a,b),r)) (x,y) = mean r (minus x a,minus y b) | mean (Inter(a,b)) pt = (mean a pt) andalso (mean b pt) | mean (Union(a,b)) pt = (mean a pt) orelse (mean b pt);

Page 9: Generating Efficient code

Staging the meaning functionfun mean2 Empty (x,y) = <false> | mean2 Univ (x,y) = <true> | mean2 (Circle r) (x,y) = <leq (plus (square ~x) (square ~y))

(square ~(lift r))> | mean2 (Rect(w,h)) (x,y) = <(between (neg w) (plus ~x ~x) ~(lift w)) andalso (between (neg h) (plus ~y ~y) ~(lift h))> | mean2 (Trans((a,b),r)) (x,y) = mean2 r (<minus ~x ~(lift a)> ,<minus ~y ~(lift b)>) | mean2 (Inter(a,b)) pt = <~(mean2 a pt) andalso ~(mean2 b pt)> | mean2 (Union(a,b)) pt = <~(mean2 a pt) orelse ~(mean2 b pt)>;

Page 10: Generating Efficient code

The results ofval tr1 = <fn (x,y) => ~(mean2 r1 (<x>,<y>))>;

-| val tr1 = <(fn (b,a) => if %leq (%plus (%square (%minus b (~2.0))) (%square (%minus a (0.0)))) (%square (1.0)) then true else if if %leq (%plus (%square (%minus (%minus b (~2.0)) (2.0))) (%square (%minus (%minus a (0.0)) (0.0)))) (%square (1.0)) then true else if if %leq (%plus (%square (%minus (%minus (%minus b (~2.0)) (2.0)) (2.0))) (%square (%minus (%minus (%minus a (0.0)) (0.0)) (0.0)))) (%square (1.0)) then true else false then true else false then true else false)> : <(real * real) -> bool>

Page 11: Generating Efficient code

Problems

• (if x then true else false)

• (minus x 0)

• (if (if a then b else c) then x else y)

• (square 1.0)

• Code duplication. (minus b 2.0) occurs many times.

Page 12: Generating Efficient code

What cause this?

– The if-then-else come from the “andalso” and the “orelse”

– We might do better to introduce two operators• conj x y = x andalso y• disj x y = x orelse y

| mean3 (Inter(a,b)) pt = <conj ~(mean3 a pt) ~(mean3 b pt)>

| mean3 (Union(a,b)) pt =

<disj ~(mean3 a pt) ~(mean3 b pt)>;

Page 13: Generating Efficient code

We now get, but not much better<(fn (b,a) => %disj (%leq (%plus (%square (%minus b (~2.0))) (%square (%minus a (0.0)))) (%square (1.0))) (%disj (%leq (%plus (%square (%minus (%minus b (~2.0)) (2.0))) (%square (%minus (%minus a (0.0)) (0.0)))) (%square (1.0))) (%disj (%leq (%plus (%square (%minus (%minus (%minus b (~2.0)) (2.0)) (2.0))) (%square (%minus (%minus (%minus a (0.0)) (0.0)) (0.0)))) (%square (1.0))) false)))> : <(real * real) -> bool>

Page 14: Generating Efficient code

Define staged datatypes

datatype Sreal = Rknown of real | Rminus of Sreal * real | Rplus of Sreal*Sreal | Rsquare of Sreal | Rneg of Sreal | Rcomplex of <real>;

datatype Sbool = Bknown of bool | Bconj of Sbool * Sbool | Bdisj of Sbool * Sbool | Bbetween of Sreal * Sreal * Sreal | Bleq of Sreal*Sreal;

Page 15: Generating Efficient code

From staged datatype to code

• The staged datatype is an abstract version of the type of answers (the abstraction)

• It contains more information than <answer>, but less information then answer

• This means we can translate from the staged datatype to <answer> but not from the abstraction to answer.

• This is called “concretion”

Page 16: Generating Efficient code

Abstraction forms a lattice

• Top of the lattice nothing is known in the first stage.

• At the bottom everything is known

• Between we have varying degrees of knowledge.

real

<real>

Rknown 4

Rplus(<x>,3)

Rplus(<x>,<y>)

Rplus(<x>,<y>)

Page 17: Generating Efficient code

Concretion functionsfun concReal (Rknown r) = lift r | concReal (Rminus(x,y)) = <~(concReal x) - ~(lift y)> | concReal (Rplus(x,y)) = < ~(concReal x) + ~(concReal y) > | concReal (Rsquare x) = <square ~(concReal x) > | concReal (Rneg x) = <neg ~(concReal x) > | concReal (Rcomplex z) = z;

fun concBool (Bknown r) = lift r | concBool (Bconj(x,y)) = <conj ~(concBool x) ~(concBool y)> | concBool (Bdisj(x,y)) = <disj ~(concBool x) ~(concBool y) > | concBool (Bbetween(x,y,z)) = <between ~(concReal x) ~(concReal y) ~(concReal z) > | concBool (Bleq(x,y)) = <leq ~(concReal x) ~(concReal y) >;

Page 18: Generating Efficient code

Optimizing functions

• With the extra information we can build smart abstraction building functions

fun rminus x 0.0 = x | rminus x y = Rminus (x,y);

fun rsquare (Rknown 1.0) = Rknown 1.0 | rsquare x = Rsquare x;

fun bconj (Bknown true) x = x | bconj x (Bknown true) = x | bconj (Bknown false) _ = Bknown false | bconj _ (Bknown false) = Bknown false | bconj x y = Bconj(x,y);

Page 19: Generating Efficient code

Now redo the generatorfun mean4 Empty (x,y) = Bknown false | mean4 Univ (x,y) = Bknown true | mean4 (Circle r) (x,y) = bleq (rplus (rsquare x) (rsquare y)) (rsquare (Rknown r) | mean4 (Rect(w,h)) (x,y) = bconj (bbetween (Rneg (Rknown w)) (rplus x x) (Rknown w)) (bbetween (Rneg (Rknown h)) (rplus y y) (Rknown h)) | mean4 (Trans((a,b),r)) (x,y) = mean4 r (rminus x a,rminus y b) | mean4 (Inter(a,b)) pt = bconj (mean4 a pt) (mean4 b pt) | mean4 (Union(a,b)) pt = bdisj (mean4 a pt) (mean4 b pt);

Not the use of the optimizing constructors

Page 20: Generating Efficient code

val tr3 = <fn (x,y) => ~(concBool (mean4 r1 (Rcomplex <x> ,Rcomplex <y>)))>;

-| val tr3 = <(fn (b,a) => %disj (%leq ((%square ((b %- ~2.0)) %+ %square a)) (1.0)) (%disj (%leq ((%square (((b %- ~2.0) %- 2.0)) %+ %square

a)) (1.0)) (%leq ((%square ((((b %- ~2.0) %- 2.0) %- 2.0)) %+

%square a)) (1.0))))> : <(real * real) -> bool>

• Better but we still have the duplicate code problem. This requires a different approach.

Page 21: Generating Efficient code

Gib – a generalization of fibbonacci

fun plus n x y = x+y;

fun gib (n,x,y) = case n of 0 => Return mm x | 1 => Return mm y | _ => Do mm { a1 <- gib (n-2,x,y) ; a2 <- gib (n-1,x,y) ; Return mm <plus ~(lift n) ~a1 ~a2 > };

Note that plus does not use its first argument “n”.

We use the first argument to plus as a

marker

Page 22: Generating Efficient code

fun testGib n = <fn (x,y) => ~(runMM (gib (n,<x>,<y>)))>;testGib 6;

val it = <(fn (b,a) => %plus 6 (%plus 4 (%plus 2 b a) (%plus 3 a (%plus 2 b a))) (%plus 5 (%plus 3 a (%plus 2 b a)) (%plus 4 (%plus 2 b a) (%plus 3 a (%plus 2 b a)))))> : <('1 * '1 ) -> '1 >

Note the code duplication. It doesn’t

come from our staging duplicating code, but

from the way the algorithm works

Page 23: Generating Efficient code

Classic case for Memoizationfun gib_ksp (n,x,y) = Do mm { a <- look n ; case a of SOME z => Return mm z | NONE => case n of 0 => Return mm x | 1 => Return mm y | _ => Do mm { a1 <- gib_ksp (n-2,x,y) ; a2 <- gib_ksp (n-1,x,y) ; memo n < plus ~(lift n) ~a1 ~a2 > } };

Memo looks up the answer stored under “n” and uses it if its there. If

not, it computes an answer and stores it in

the table.

Page 24: Generating Efficient code

Observefun test_ksp n = <fn (x,y) => ~(runMM (gib_ksp (n,<x>,<y>)))>;test_ksp 6;

val it = <(fn (b,a) => let val c = %plus 2 b a val d = %plus 3 a c val e = %plus 4 c d val f = %plus 5 d e val g = %plus 6 e f in g end)> : <('1 * '1 ) -> '1 >

Where did the “let”s come

from?

Page 25: Generating Efficient code

Questions

• Can we somehow hide the messiness of this?

• Can we make it work with the abstraction mechanism we used in the Region language?

Page 26: Generating Efficient code

A Monad can hide somethings

• The table that code is stored in– The table is indexed by “n”

• The ability to locally name things using “let”, and then use the name instead of the code.

Page 27: Generating Efficient code

Better Yetfun gib_best (n,x,y) = try2 n (case n of 0 => Return mm x | 1 => Return mm y | _ => Do mm { a1 <- gib_best (n-2,x,y) ; a2 <- gib_best (n-1,x,y) ; Return mm <plus ~(lift n) ~a1 ~a2 > });

fun test_best n = <fn (x,y) => ~(runMM (gib_best (n,<x>,<y>)))>;test_best 6;

<(fn (b,a) => let val c = %plus 2 b a val d = %plus 3 a c val e = %plus 4 c d val f = %plus 5 d e val g = %plus 6 e f in g end)>

The try2 function hides the case

analysis. Note the code looks just like

the original

Page 28: Generating Efficient code

try2

fun try2 n exp =

Do mm { a <- look n

; case a of

SOME z => Return mm z

| NONE => Do mm { ans <- exp

; memo n ans}

};

Page 29: Generating Efficient code

The Monad

• ‘a is what’s computed• ‘s is the memo table• ‘c is what we eventually want

datatype ('a,'s,'c) mm = MM of ('s -> ('s -> 'a -> 'c) -> 'c);fun unMM (MM f) = f;

fun mmReturn a = MM (fn s => fn k => k s a);fun mmBind (MM a) f = MM(fn s => fn k => a s (fn s' => fn b => unMM (f b) s' k));val mm = Mon(mmReturn,mmBind);

Page 30: Generating Efficient code

Operation in the monad

• Find some thing in the memo tablefun look x = let fun h s k = k s (lookup s x) in MM h end;

• Add some value “x” to table indexed under “n”fun memo n x = let fun h s k = <let val z = ~x in ~(k (ext s (n,<z>)) <z>) end> in MM h end;

His is where the “let”s are generated.

Page 31: Generating Efficient code

Just name it

fun name x =

let fun h s k =

<let val z = ~x in ~(k s <z>) end>

in MM h end;

Page 32: Generating Efficient code

For examplefun pow 0 i = Return mm <1> | pow 1 i = Return mm i | pow n i = if even n then Do mm { x <- pow (n div 2) i ; Return mm < ~x * ~ x> } else Do mm { x <- pow (n-1) i ; Return mm < ~i * ~ x> }

<(fn a => a %* a %* a %* a %* a %* a %* a %* a %* a %* a)>

Page 33: Generating Efficient code

Compare withfun powN 0 i = Return mm <1> | powN 1 i = Return mm i | powN n i = if even n then Do mm { x <- powN (n div 2) i ; name < ~x * ~ x> } else Do mm { x <- powN (n-1) i ; name < ~i * ~ x> }

<(fn a => let val b = a %* a val c = b %* b val d = a %* c val e = d %* d in e end)>

Page 34: Generating Efficient code

Adding Abstract Code

• Like before, Abstract code is a staged datatype for arithmetic expressions.

datatype 'a Code = Simple of < 'a > (* a variable *) | Complex of < 'a > (* a non-trivial exp *) | Known of 'a | Factor of real * 'a Code | Neg of 'a Code;

Page 35: Generating Efficient code

Optimization

• Optimization has two parts.– A walk over the whole tree to reach every

node– Applying the smart constructors that apply

local optimizations for each kind of node.– The “smart constructors” are italized (and in

red if you’re viewing this in color)

Page 36: Generating Efficient code

Abstraction Optimizationfun compress (Simple x) = Simple x | compress (Complex x) = Complex x | compress (Known x) = Known x | compress (Add(x,y)) = let fun help (a as (Factor(n,x))) (b as (Add(Factor(m,y),z))) = if n=m then help (Factor(n,add x y)) z else add a b | help a b = add a b in cast (help (cast (compress x)) (cast (compress y))) end | compress (Sub(x,y)) = cast (sub (cast(compress x)) (cast(compress y))) | compress (Factor(n,x)) = cast (mult (cast (Known n)) (cast (compress x)))

Page 37: Generating Efficient code

Example smart constructor

fun mult (Known 0.0) y = Known 0.0 | mult y (Known 0.0) = Known 0.0 | mult (Known 1.0) y = y | mult y (Known 1.0) = y | mult (Known ~1.0) (Sub(a,b)) = sub b a | mult (Sub(a,b)) (Known ~1.0) = sub b a | mult (Known a) (Factor(b,x)) = mult (Known (a*b)) x | mult (Factor(b,x)) (Known a) = mult (Known (a*b)) x | mult (Known x) c = Factor(x,c) | mult c (Known x) = Factor(x,c) | mult (Factor(c,x)) (Factor(d,y)) = Factor(c*d,mult x y) | mult (Factor(n,c)) d = Factor(n,mult c d) | mult d (Factor(n,c)) = Factor(n,mult c d) | mult x y = Complex < ~(cast(conc (cast x))) * ~(cast(conc (cast y))) >

Page 38: Generating Efficient code

Details

• In fact, the optimizer (compress) and the smart constructors (add,sub,mult), and the concretion function must be mutually recursive since they call each other.

Page 39: Generating Efficient code

The concretion functionfun conc x = let fun f (Simple x) = x | f (Complex x) = x | f (Known x) = lift x | f (Factor(~1.0,c)) = cast < unmin ~(cast (conc c)) > | f (Factor(n,c)) = cast < ~(lift n) * ~(cast (conc c)) > | f (Add(x,y)) = cast < ~(cast(conc x)) + ~(cast(conc y)) > | f (Sub(x,y)) = cast < ~(cast(conc x)) - ~(cast(conc y)) > in f x end

Page 40: Generating Efficient code

Working together• The Monad and the abstract code can be made to work together.nameC : 'c Code -> ('c Code,'a ,<'b >) mm

fun nameC x =

let fun f (x as (Simple _)) = mmReturn x

| f (x as (Known _)) = mmReturn x

| f (x as (Factor(~1.0,Simple _))) = mmReturn x

| f (x as (Factor(_,Simple _))) = mmReturn x

| f (Factor(n,a)) =

MM(fn s => fn k =>

<let val z = ~(conc a)

in ~(k s (Factor(n,Simple <z>))) end>)

| f x = MM(fn s => fn k =>

<let val z = ~(conc x)

in ~(k s (Simple <z>)) end>)

in f (compress x) end;


Recommended