25
Tail-call Optimization (TCO)

Tail call optimization (TCO) - Lecture

Embed Size (px)

Citation preview

Tail-call Optimization

(TCO)

What is tail-call optimization?

A compiler optimization that allows

the efficient use of procedure calls in

the tail position

What’s a procedure?

Blocks of callable code

Functions

Methods

Subroutines

Routines

What does it mean for a procedure

call to be in tail position?

A procedure call is in tail position if it’s

the last thing to happen before

returning

Exa

mp

le 1

public static void foo1(){

int a = bar():

return a + 1;

}

Exa

mp

le 2

public static void foo2(){

int a = bar():

return a;

}

Where does the inefficiency

come from?

Call stack

Container for storing procedure calls

Grows upward

Last in, first out (LIFO)

Composed of stack frames

Keeping with current analogies…

Each pancake represents some procedure to be

stepped through (some computation).

The plate represents boilerplate used by the runtime

system to return from procedures once they’ve run to

completion.

Boilerplate might consist of return addresses, formal

parameters, etc.

What’s in a stack frame?

Each stack frame is made of up of:

Some computation to run (a pancake)

Some boilerplate used for returning (a plate)

So our stack really looks more like this…

Specifically…

main()

a()

b()↵

Fine-tuning the analogy…

Remember, we consider a pancake

some amount of computation/work that

needs to be performed before a

procedure can return.

So we would expect the pancake to

vary in size over the course of its lifetime.

Concretely…

main()

a()

b()

;

What would the stack look like if

our functions made use of tail

calls?

Probably something like this…

main()

a()

b()

So where’s the inefficiency?

Oh no, stack overflow!

Our stack has to exist in memory, so

it has a finite size.

If we have too many stack frames,

eventually we’ll run out of room

and get a stack overflow.

A solution approaches

If procedure calls are in tail position, we can

pop their remaining stack frame, since the

plate is all that’s left.

Since stack frames aren’t something the

programmer deals with explicitly, we let the

compiler take care of the details.

Thus we have tail call elimination/optimization.

(TCE/TCO)

A comparison

Without TCO With TCO

main()

a()

b()

main()

b()

Why should I care about this?

Benefits of TCO

You can solve problems efficiently using

recursion.

You can make efficient use of function

calls within procedures.

More stack space means more room for

the procedures that aren’t able to make

use of tail calls.

More examples:

https://gist.github.com/numberten/4acb8e7e8988caba8b5d

Related Wikipedia pages

http://en.wikipedia.org/wiki/Call_stack

http://en.wikipedia.org/wiki/Tail_call

http://en.wikipedia.org/wiki/Subroutines

http://en.wikipedia.org/wiki/Mutual_recursion

http://en.wikipedia.org/wiki/Structured_programming