Ismm 2009
Ismm 2009
Abstract First, it can lead to poor utilization of memory, when one data struc-
This paper presents an analysis that derives a formula describing ture is full and another has space to spare. Second, it requires ex-
the worst-case live heap space usage of programs in a functional plicit memory management: the programmer must keep track of
language with automated memory management (garbage collec- which array entries are in use and when it is safe to mark an occu-
tion). First, the given program is automatically transformed into pied entry as available; this can be difficult for entries that may be
bound functions that describe upper bounds on the live heap space used by multiple parts of the program and referenced from multi-
usage and other related space metrics in terms of the sizes of func- ple data structures. Of course, these are some of the reasons for the
tion arguments. The bound functions are simplified and rewritten growing popularity of languages with automatic memory manage-
to obtain recurrences, which are then solved to obtain the desired ment (Java, C#, Python, Ruby, etc.) in non-embedded systems. Ver-
formulas characterizing the worst-case space usage. These recur- sions of these languages suitable for embedded systems exist and
rences may be difficult to solve due to uses of the maximum opera- are being promoted, e.g., Java Platform, Micro Edition (JavaME)
tor. We give methods to automatically solve categories of such re- and Microsoft .NET Micro Framework. While advances in real-
currences. Our analysis determines and exploits monotonicity and time garbage collection (e.g., [5]) play an essential role in making
monotonicity-like properties of bound functions to derive upper such languages practical, analyses that can accurately predict the
bounds on heap usage, without considering behaviors of the pro- worst-case space usage of programs written in garbage-collected
gram that cannot lead to maximal space usage. languages are also important for their adoption in embedded sys-
tems [13].
Categories and Subject Descriptors F.3.2 [Logics and Meanings Space analysis is important for determining space requirements
of Programs]: Semantics of Programming Languages—Program and for accurate execution-time analysis. For example, analysis
analysis of worst-case execution time in real-time systems often uses loop
General Terms Languages, Performance, Verification bounds or recursion depths, both of which are commonly deter-
mined by the size of the data being processed. Space analysis can
Keywords Live Heap Space Analysis, Functional Languages, also help determine the timing effects of memory-related events
Garbage Collection, Recurrence Relations such as memory allocation, garbage collection, cache misses, and
page faults.
1. Introduction This paper describes a general approach for automatic accurate
Analysis of the time and space requirements of computer programs analysis of heap space usage, specifically, the maximum size of live
is important for virtually all computer applications, especially in data on the heap during execution. In other words, the analysis de-
embedded systems, real-time systems, and interactive systems. The termines heap usage of programs in the presence of perfect garbage
importance of time analysis for real-time and embedded systems is collection where garbage is collected as soon as it is created. This
reflected in the long tradition of research on worst-case execution result is the minimum amount of heap space needed to run a pro-
time (WCET) analysis. Space usage is also critical in many real- gram, no matter which garbage collection scheme is used. Limiting
time and embedded systems, due to the limited amount of memory a program to this “minimum amount of heap” in the presence of
and the potential for severe consequences if the system fails due imperfect garbage collection schemes, simply means that garbage
to insufficient memory. Due in part to the difficulty of predicting collection needs to be performed intermittently to free up space for
the space usage of programs that use dynamic memory allocation, allocation. The analysis can easily be modified to determine related
real-time and embedded software typically use only statically al- metrics, such as space usage when garbage collection is performed
located data structures. However, this approach has disadvantages. only at fixed points in the program. It can also be adapted to ana-
lyze the space usage of continuously running processes with cyclic
∗ This
work was supported in part by ONR under Grant N00014-07-1-0928 behavior. Our analysis is designed for a functional language. This
and NSF under Grants CCF-0613913, CNS-0627447, CNS-0831298, and is a simplification that allows us to focus on the fundamental is-
CNS-0509230. sues first, deferring the complications needed to handle imperative
updates.
Our approach starts with a program P written in a functional
language with garbage collection. We construct, for every function
Permission to make digital or hard copies of all or part of this work for personal or f in P , bound functions that describe worst-case live heap space
classroom use is granted without fee provided that copies are not made or distributed usage of f and other measures—such as the size of the result of
for profit or commercial advantage and that copies bear this notice and the full citation f —necessary to determine the space usage of the program. The
on the first page. To copy otherwise, to republish, to post on servers or to redistribute
to lists, requires prior specific permission and/or a fee.
inputs to bound functions for f are the sizes of inputs to f . Bound
ISMM’09, June 19–20, 2009, Dublin, Ireland.
functions are simplified and rewritten to recurrence relations, which
Copyright c 2009 ACM 978-1-60558-347-1/09/06. . . $5.00 are solved into closed form expressions that describe live heap
space usage and other measures in terms of sizes of inputs to the plication f (e1 , . . . , en ) of the function f (v1 , . . . , vn ), expressions
corresponding original functions. The basic version of our analysis e1 , . . . , en are arg-exps of the call to f . For i ∈ [1, n], ei corre-
is limited to programs that manipulate non-nested lists, i.e., lists in sponds to parameter vi of f .
which every element is a value of a primitive data type. Section 12
describes how to extend the analysis to accommodate other data Argument-uses (arg-uses) of functions. An arg-use of a func-
types. tion f is an occurrence of an application of f in an arg-exp. For
Solving recurrence relations can be difficult. The availability example, g(1 + f (x), 2) contains an arg-use of f which corre-
of increasingly sophisticated recurrence solvers [21, 6], is an as- sponds to the first parameter of g. Arg-uses of expressions, e.g.,
set to our analysis. We use methods such as elimination of redun- g(if p then e1 else e2), are defined similarly.
dant arguments from recursive bound functions and recurrences to
improve solvability. This may even eliminate dependence on, and Argument-users (arg-users) of functions. In a function applica-
hence the need to solve, certain recurrences. Since we are interested tion f (e1 , . . . , en ), if ei contains an arg-use of a function g, then f
in worst-case heap space usage, bound functions and their recur- is called an arg-user of g. For example, in f (1 + g(x), 2), f is an
rences may contain the max operator, which is typically not han- arg-user of g. Arg-users of expressions are defined similarly.
dled (or not handled exactly) by existing recurrence solvers such as
[21, 6]. We use templates based on pattern matching and inductive Dependence. A function f is dependent on a function g if there
reasoning to solve such recurrences. Results of some bound func- is a path from f to g in the call graph of f .
tions are used as arguments to others. Maximizing the results of the
former bound functions leads to maximal live heap space usage for Monotonicity. Consider function f : X1 × X2 . . . × Xm → Y ,
the program only if the latter bound functions are monotonically in- where X1 , ..., Xm , and Y are sets of whole numbers, integers or
creasing with respect to their arguments. Monotonicity properties real numbers. f is monotonically increasing with respect to its ith
of bound functions are ascertained from their closed form solutions, argument, if
if known, and using templates for bound functions still expressed
as recurrences. ∀ a, b ∈ Xi , c1 ∈ X1 , . . . , cm ∈ Xm . a ≤ b ⇒
f (c1 , c2 , . . . , a, . . . , cm ) ≤ f (c1 , c2 , . . . , b, . . . , cm )
2. Language where a and b are the i’th argument of f . We use the terms mono-
We formalize the analysis for a first-order, call-by-value functional tonic and monotonically increasing interchangeably.
language that has literal values of primitive types (e.g., Boolean
and integer constants), operations on primitive types (e.g., addition
and subtraction), data constructors (e.g., null for the empty list, and 4. Bound Functions
cons to construct lists), testers (which test whether a value is built Consider function f (v1 , ..., vn ) = ef in input program P . We de-
using a particular constructor), selectors (which extract parts of data fine three bound functions for f . Bound functions of f take as ar-
structures), conditionals, bindings (i.e., local variable declarations), guments, the sizes of arguments v1 , ..., vn of f . We call these size
and function calls. A program is a set of function definitions of arguments and denote them by v1s , ..., vns . The live heap space
the form f (v1 , ..., vn ) = e, where an expression e is given by the bound function of f , Sf , called simply the S function of f , de-
grammar scribes the worst-case heap usage of f over all possible combina-
e ::= v variable tions and values of arguments v1 , ..., vn having sizes v1s , ..., vns ,
| l literal respectively.
| cons(e1 , e2 ) constructor application
| prim(e1 , . . . , en ) primitive operation Sf (v1s , . . . , vns )
| null?(e) tester = upper bound (min heap to evaluate f (a1 , ..., an ))
all args a1 ,...,an to f
| car(e) first element of list
s.t. ∀i∈[1,n]. |ai |=vis
| cdr(e) tail of list
| if p then e1 else e2 conditional expression
| let v1 = e1 , . . . , vn = en in e binding expression The worst-case heap usage corresponds to the maximum size of
| f (e1 , . . . , en ) function application the live heap seen during the evaluation of f . This is also the
minimum heap space required to evaluate f on arguments of sizes
Predicates in conditional expressions are given by the grammar v1s , ..., vns , no matter which garbage collection scheme is used. A
p ::= v | l | prim(p1 , . . . , pn ) | null?(p) | car(p) | cdr(p) heap object is live as long as it can be reached from the program
through function arguments or the reference to the result of the
The basic version of our analysis supports recursive functions most recently evaluated expression. The live heap space usage of
but not mutual recursion. Section 12 describes how to handle mu- program P is described by Sg , where g is the entry function of P .
tual recursion. The new result-space bound function of f , also called the N
Our analysis makes use of type information, which may be function of f , denoted Nf , describes the newly-allocated space
obtained from type declarations or from automated type inference. in the result of f in terms of the sizes of f ’s arguments, and the
type(v) denotes the type of variable v. rtype(f ) denotes the return amounts of newly-allocated space in these arguments. For function
type of function f . The allowed types are Bool, Int and List. call f (e1 , ..., en ), Nf returns the number of new heap cells in the
result r of the call. A heap cell in r is new if it is created after
3. Definitions the start of evaluation of f (e1 , ..., en ), i.e., created in the body of
f or in one of the arg-exps e1 , ..., en . So, in addition to its size
Data Sizes. The size of an empty list is 0. The sizes of all other
arguments, Nf also has newsize arguments, denoted v1w , ..., vnw ,
atomic objects (booleans and numbers) are their values. The size of
representing the number of new heap cells in each argument. For
a list is its length.
f (e1 , ..., en ), viw is the number of heap cells in vi created during
Argument-expression (arg-exp). An arg-exp is an expression evaluation of expression ei . viw is meaningful only if vi is a list;
whose result is an argument to a function call. In a function ap- if not, then there are no heap cells in vi and viw is not required or
used.
For f (v1 , ..., vn ) = e, Sf (v1s , ..., vns ) = S [e]].
Nf (v1s , . . . , vns , v1w , . . . , vnw ) S [l]] = S [nil]] = S [v]] = 0
amount of new heap in the result of
= upper bound S [cons(e1 , e2 )]] = 1 + max(S [e1] , S [e2])
f (a1 , ..., an )
all args a1 ,...,an to f S [prim(e1 , ..., en )]] = max(S [e1] , ..., S [en])
s.t. ∀i∈[1,n]. |ai |=vis
S [null?(e)]] = S [car(e)]] = S [cdr(e)]] = S [e]]
|new heap in ai |≤viw
S [ifp then e1 else e2 ]
The size bound function of f , also called the R function of f , max(S [e1] , S [e2]), if p contains car
=
denoted Rf , describes the size of the result of f in terms of the if R [p]] then S [e1] else S [e2] , otherwise
sizes of f ’s arguments. S [let v1 = e1 , . . . , vn = en in e]]
Rf (v1s , ..., vns ) = upper bound |f (a1 , . . . , an )| = max(S [e1] ,
all args a1 ,...,an to f N [e1] [0/vw ] + S [e2] ,
s.t. ∀i∈[1,n]. |ai |=vis ..
.
The bodies of Sf , Nf , and Rf are obtained by applying transfor- N [e1] [0/vw ] + . . . + N [en−1] [0/vw ] + S [en] ,
mations S, N , and R, respectively, to the body ef of f . The defi- N [e1] [0/vw ] + . . . + N [en] [0/vw ]+
nitions of transformations S, N and R. are provided in Sections 5, S [e]] [R [e1] /v1s , ..., R [en] /vns ])
6, and 7. S [f (e1 , ..., en )]]
S [e]]: Computes an upper bound on the live heap required for = max(S [e1] ,
evaluation of e. N [e1] [0/vw ] + S [e2] ,
..
N [e]]: Computes an upper bound on the number of new heap cells .
in result r of e. If e occurs in the body of function f , then a heap N [e1] [0/vw ]... + N e(n−1) [0/vw ] + S [en] ,
cell in r is new if it is created in e, or if it is new in v1 , ..., vn , N [e1] [0/vw ]... + N [en] [0/vw ]+
which can easily be determined using v1w , ..., vnw . Since there is Sf (R [e1] , ..., R [en]))
no imperative update in our target language, any node that points to
another node must be newer than the latter. So, for a list argument
vi , only the first viw nodes are new. Figure 1. Transformation S. e[0/vw ] substitutes every newsize
argument vw in e with 0.
R [e]]: Computes an upper bound on the size of the result of e.
R [e]] is boolean if e returns a boolean and is numeric if e returns a
number or a list.
6. New Result-Space Bound Functions
5. Live Heap Space Bound Functions Transformation N , defined in Figure 2, produces new result space-
Transformation S, defined in Figure 1, is used to derive live heap bound functions. If function f in the input program returns prim-
space bound functions. In the S transformation of conditionals, if itive data, then its result uses no heap space and Nf returns 0. N
the predicate contains car then its truth value is unknown, because functions exploit the fact that, in the absence of imperative update,
the analysis does not track the values of list elements. The heap newer heap cells reference older heap cells. So, if a list v con-
space required to evaluate the conditional is the maximum of that tains vw new heap cells, it is the first vw cells of v that are new.
required for the branches (the grammar in Section 2 implies that Newsize arguments are decremented in recursive calls to N func-
predicates in conditional expressions do not perform heap alloca- tions if corresponding size arguments are, e.g., N [f (cdr(ls))]] is
tion). If the predicate does not contain car, then we maintain the Nf (lss − 1, lsw − 1). Recursion often proceeds until lss = 0,
conditional structure, and transform the predicate and the branches. which implies decrementing lss from lsw . Only the first lsw of lss
Such predicates are often tests of recursive functions that control re- cells are new. So, all decrements to newsize arguments are enclosed
cursion, e.g., base case tests or tests that determine the next recur- by applications of max(0, ...). See Figure 4 for examples of func-
sion step. The transformed conditionals in bound functions even- tions in an input program and corresponding N functions.
tually (after simplification) become multiple cases in the definition N functions may sometimes yield recurrences that are diffi-
of a recurrence. The original predicate p in the conditional expres- cult to solve, because of the large number of arguments (size and
sion is transformed into a predicate over size arguments. N and R newsize arguments) and the presence of max(0, ...) expressions.
transformations of conditionals are similar. If Nf cannot be solved, we redefine it as Nf (v1s , ..., vns ) =
In function application f (e1 , ..., en ), arg-exps e1 , ..., en are Rf (v1s , ..., vns ). In other words, instead of determining how much
evaluated in order, followed by the call to f . The heap usage of of the list returned by f is new, we over-approximate, saying that
f (e1 , ..., en ) is the maximum of the heap usages of the arg-exps the whole list is new. In all our examples, these simpler N func-
e1 , ..., en and the heap usage of f when called with arguments tions, wherever used, do not introduce overestimations in the anal-
whose sizes are those of the results of e1 , ..., en . When ei , i ∈ ysis result. The redefinition does not lose accuracy in practice, be-
[2, n], is evaluated, the results of the previous arg-exps are live. So, cause our analysis determines worst-case space usage, and in the
the maximum size of the live heap during evaluation of ei is the sum worst case, most functions either build completely new lists or the
of the newly-allocated space in the results of previous arg-exps and arguments given to functions are completely new; in both cases, all
S [ei]. N [ei] [0/vw ] is the amount of new heap in vi that is allo- cells in the result lists are new. This is in keeping with the following
cated in ei . Newsize arguments are neither required nor applicable observation:
and they are substituted with 0s. The function call is evaluated after Nf (v1s , ..., vns , v1s , ..., vns ) = Rf (v1s , ..., vns )
all arg-exps, and this takes space Sf (R [e1] , ..., R [en]).
See Figure 4 for examples of functions in an input program and where the values of the newsize arguments of Nf equal those of the
corresponding S functions. corresponding size arguments.
For f (v1 , ..., vn ) = e, For f (v1, ..., vn) = e, Rf (v1s , ..., vns ) = R [e]]
0, if rtype(f ) 6= List R [l]] = l, R [nil]] = 0, R [v]] = vs
Nf (v1s , ..., vns , v1w , ..., vnw ) =
N [e]] , otherwise
R [cons(e1 , e2 )]] = 1 + R [e2]
N [l]] = N [nil]] = 0 R [prim(e1 , ..., en )]] = prim(R [e1] , ..., R [en])
0, if type(v) 6= List R [null?(e)]] = eq?(R [e]] , 0)
N [v]] =
vw , otherwise
R [car(e)]] = uk, R [cdr(e)]] = R [e]] − 1
N [cons(e1 , e2 )]] = 1 + N [e2]
R [ifp then e1 else e2]
N [prim(e1 , ..., en )]] = 0 max(R [e1] , R [e2]), if p contains car
N [null?(e)]] = N [car(e)]] = 0 =
if R [p]] then R [e1] else R [e2] , otherwise
N [cdr(e)]] = max(0, N [e]] − 1) R [let v1 = e1 , . . . , vn = en in e]]
N [if p then e1 else e2] = (R [e]]) [R [e1] /v1s , . . . , R [en] /vns ]
max(N [e1] , N [e2]), if p contains car R [f (e1 , . . . , en )]] = Rf (R [e1] , . . . , R [en])
=
if R [p]] then N [e1] else N [e2] , otherwise
N [let v1 = e1 , . . . , vn = en in e]] Figure 3. Transformation R.
= (N [e]]) [R [e1] /v1s , ..., R [en] /vns ,
N [e1] /v1w , ..., N [en] /vnw ]
N [f(e1 , . . . , en )]]
0, if rtype(f ) 6= List elements of ls that are less than or equal to x.
=
Nf (R [e1] , ..., R [en] , N [e1] , ..., N [en]), otherwise
m if n = 0
Rappend (n, m) =
1 + Rappend (n − 1, m) otherwise
Figure 2. Transformation N .
0 if n = 0
Rlls (n) =
max(1 + Rlls (n − 1), Rlls (n − 1)) otherwise
7. Size Bound Functions Given recurrence T , we define recurrence T all that has the same
Transformation R in Figure 3 produces size bound functions; recall arguments as T and returns the set of results obtained by using the
that Rf bounds the size of the result (return value) of f . See figure value of each argument of max in place of the max expression in
4 for examples of functions in an input program and corresponding T . Note that this is done recursively, not only at the top-level call.
R functions. From the definitions of transformations S, N , and R, T , on the other hand, uses only the largest of the arguments to max
it can be seen that results of R functions are used by S and N at every recursion level.
functions only as the values of size arguments. Also, recall that we Intuitively, T captures only the maximum value of the size or
are interested only in the maximal value of the input program’s S space measure for a function in the input program, while T all cap-
function. Consider the R transformation of conditionals. Predicates tures all possible values of the size or space measure for that func-
containing car are concluded as being undeterminable and in such tion. We can generate the definition of T all from the definition of
cases, the size of the result of the conditional is the maximum of T , by replacing each occurrence of max with union and replac-
the sizes of e1 and e2 , max(R [e1] , R [e2]). Ideally, we should ing every function call F (. . .) with F all (. . .); note that upper case
return that size which helps S functions determine the worst-case letters (F , G, H) are used to denote bound functions. These re-
(maximal) space usage of the input program. But it is impossible placements in the definition of T need to be accompanied by mod-
to know which size to return, in the general case. Further, it is not ifications that ensure type-safety, for instance, the replacement of
tractable to return all sizes. Thus, the use of max in the definition primitive operators and functions with ones that are overloaded to
of bound functions is a necessary approximation, and based on the handle regular argument values as well as sets of argument values.
somewhat reasonable expectation that the maximum size leads to For example, for sets X and Y of argument values to f , f (X, Y ) =
maximum space-usage results of dependent S and N functions. {f (x, y) | x ∈ X, y ∈ Y }, and f (x, Y ) = {f (x, y) | y ∈ Y }.
We perform checks, namely MC1, MC2, and MC3 described later Recurrences Rlls and Rlls all
and their solutions are shown below.
in this section, to verify if this is indeed the case, i.e., if the use
of max in R functions results in dependent S and N functions 0 if n = 0
Rlls (n) =
determining true upper bounds on space measures. Transformation max(1 + Rlls (n − 1), Rlls (n − 1)) otherwise
R, along with checks MC1, MC2, and MC3, ensure that all R = n
functions, including ones whose definitions do not contain max
all {0} if n = 0
but are dependent on R functions that do, uniformly return upper Rlls (n) = all all
bounds (maximums) on size measures. ∪(1 + Rlls (n − 1), Rlls (n − 1)) otherwise
= {n, n − 1, . . . , 0}
Composite recurrences. A composite recurrence is a recurrence
(or a definition of a bound function) which contains an application A composite recurrence T is simple if the solution of T all is
of max in which at least one arg-exp contains a recursive call to a singleton set for all evaluations of its arguments; otherwise, we
the function defined by the recurrence. A regular recurrence is one say that T is complex. If Rf is a simple composite recurrence, then
that does not contain such occurrences of max. The R function the size of the result of f is uniquely determined by the sizes of its
Rappend of append, which appends one list to another, is regu- arguments. If Rf is a complex composite recurrence, then the size
lar, while Rlls , the R function of lesserorequal-list is composite. of the result of f depends on the actual elements in list arguments
all
lesserorequal-list, an auxiliary function of quick-sort, takes a list ls of f . The solution of Rlls is not a singleton set and therefore, Rlls
and a cutoff x as arguments and returns a new list containing the is complex. The R function Rins of insert which inserts a number
insertion-sort insert
insertion-sort(ls) = if null?(ls) then nil insert(x, ls) = if null?(ls) then cons(x, nil)
else insert(car(ls), else if lessoreq?(x, car(ls))
insertion-sort(cdr(ls))) then cons(x, ls)
else cons(car(ls), insert(x, cdr(ls)))
Nis (lss ) = if lss = 0 then 0 else Nins (Ris (lss − 1)) Nins (lss ) = Rins (lss ) = 1 + lss
= lss
Ris (lss ) = if lss = 0 then 0 else Rins (Ris (lss − 1)) Rins (lss ) = if lss = 0 then 1 + 0
0 if lss = 0 else max(1 + lss , 1 + Rins (lss − 1))
=
1 + Ris (lss − 1) otherwise 1 if lss = 0
=
= lss max(1 + lss , 1 + Rins (lss − 1)) otherwise
= 1 + lss
Figure 4. Entry function insertion-sort and auxiliary function insert of program insertion-sort, followed by their bound functions, recur-
rences (if any) obtained from bound functions, and closed form solutions of recurrences.
into a sorted list is a simple composite recurrence, as shown below. it is not always the same as the result of F (e1 , . . . , en ) (since
1 if n = 0 F = max o F all ). Consider function G1 (v) = v − F1 (v) as an
Rins (n) = example. The expression ‘v−F1 (v)’ is the result of multiple rounds
max(1 + n, 1 + Rins (n − 1)) otherwise
of simplification, and this explains the occurrence of the result of
an R function in a non-recursive expression that defines G1 which
{1}, if n = 0
all
Rins (n) = all could be an S or N function. The result of max o Gall 1 , which
∪({1 + n}, 1 + Rins (n − 1)) otherwise is the desired result of G1 , is obtained by replacing ‘F1 (v)’ in the
= {1 + n} body of G with the minimum element of F1all (v). Thus, the derived
We determine if composite recurrences are simple or complex using definitions of bound functions such as G1 do not necessarily yield
templates. This is described in Section 9. the desired upper bounds because of the use of max in called R
functions. So, for every complex R function F for which F =
Complex R functions. An R function is complex if it is defined max o F all , we perform the following checks to ensure that the
by a complex composite recurrence or it depends on another com- definitions of bound functions dependent on F yield upper bounds
plex R function or its solution contains an application of max over on relevant space and size measures.
closed forms; otherwise, we say that the R function is simple. R
functions Rappend and Rins are simple, while Rlls is complex. MC1 All expressions containing calls to F are monotonically in-
Rqs , shown below, is a complex R function even though the recur- creasing with respect to the calls to F (e.g., 1+F (. . .) is mono-
rence defining it is regular because of the dependence on complex tonic with respect to the call to F , but n − (1 + F (. . .))) is
R function Rlls . not. This check ensures that values of size arguments of S and
Rqs
(lss ) N functions, determined using expressions containing applica-
tions of complex R functions, are maximized.
lss if lss = 0 or lss = 1
= MC2 For every arg-use of F by a function H, occurring in the body of
1 + Rqs (Rlls (lss − 1)) + Rqs (Rgls (lss − 1)) otherwise
G, the solution of H should be monotonically increasing with
8. Conditions on Uses of Complex R Functions respect to the argument corresponding to the arg-use of F . An
example from quick-sort:
We observe that for an R function F that does not depend on any
complex R functions (in other words, does not call any R function Nqs (lss , lsw )
or depends only on simple R functions), F = max o F all . Con- = . . . Napp (. . . Rlls (lss − 1), . . . Rgls (lss − 1))
sider the following definition of a bound function G that depends
on a complex R function F , for which F = max o F all . qs, app, lls, and gls are abbreviations for quick-sort, append,
lesserorequal-list, and greater-list, respectively. Rlls and Rgls
G(. . .) = . . . F (e1 , . . . , en ) . . . are complex R functions. So, the above definition of Nqs is
From the discussion in Section 7, the desired result of G is that correct only if Napp is monotonically increasing with respect
of max o Gall , whether G is an S or N or R function. For to both its arguments (which it is, because Napp (ls1s , ls2w ) =
this to be true, in place of F (e1 , . . . , en ) in the body of G, we ls1s + ls2w ).
should use the specific size s ∈ F all (e1 , . . . , en ) that maximizes MC3 For every arg-use of F by function G, occurring in the body of
the result of evaluation of the body of G. This desired size s is G, we need to ascertain from the definition of G, that the use of
not necessarily the maximum element in F all (e1 , . . . , en ), i.e., the result of F (in other words, max o F all ) yields the desired
maximum value, namely the result of max o Gall . An example N function is Nf (lss , lsw ) = 1 + max(0, (lsw − 1)). In particu-
from quick-sort: lar, newsize arguments to N functions only get added to the result;
Sqs (ls non-monotonic operators are never applied to them. Further, new-
s) size arguments have no counterparts in the original program, unlike
lss if lss = 0 or lss = 1
size arguments of numeric variables where operators applied to the
= max(2lss −1 + lss − 1 + Sqs (Rgls (lss − 1)), latter may translate into the same operators applied to the former
3.2lss −1 − 2), otherwise
in the definitions of bound functions. Predicates of conditionals
The definition of Sqs contains the recursive call Sqs (Rgls (lss − could result in recurrences that are non-monotonic with respect to
1)). Rgls is a complex R function. The result of Rgls (equal to the arguments in the predicates; however, newsize arguments do
that of max o Rgls all
) can be used only if we can ascertain that not occur in the predicates of conditional expressions after trans-
this will result in the body of Sqs evaluating to max o Sqs all
. formation by N (note from the definition of N that predicates are
Templates are used to determine such properties (see . Tem- transformed by R). Arg-exps of functions are transformed by R to
plates described in Section 10) are used to determine if MC3 obtain size arguments to corresponding bound functions. So, new-
holds for a given recurrence G. They do so by checking if size arguments are never used as size arguments with respect to
certain requirements, such as the following, are met: G con- which bound functions can be non-monotonic.
tains only safe patterns of recursion, base cases of G are in-
creasing, and closed form expressions added to results of re- 9. Solving Composite Recurrences
cursive calls to G are monotonically increasing (this pertains We developed a set of templates to solve composite recurrences.
to “2lss −1 + lss − 1” in the definition of Sqs ). These require- The templates are designed to match composite recurrences of the
ments ensure that recurring on a large argument value (in place forms that commonly arise in bound functions, such as from the S
of the result of the complex R function) leads to a greater re- transformation of function applications (see Figure 1). The applica-
sult of evaluation of the body of G, than recurring on a small bility of a template is defined by a pattern that the recurrence must
argument value. match, together with some requirements that must be satisfied by
Monotonicity properties in MC1 and MC2 follow directly from the constants and expressions in the recurrence. Several templates
monotonicity properties of primitive operations, e.g., +, × are solve composite recurrences by reducing the given composite re-
monotonic with respect to their arguments, −, ÷ are monotonic currence into a regular recurrence containing a single argument e
with respect to their first arguments but not with respect to their of the max application, such that the evaluation of e is the maxi-
second arguments, and modulo is not monotonic with respect to mum at the relevant base cases (different from the base cases of the
either of its two arguments. given recurrence), as well as the inductive case. Other templates
Consider a bound function G such that for every R function F work by reducing composite recurrences with multiple max ap-
that G depends on, F = max o F all . If checks MC1, MC2, and plications into component composite recurrences which are recur-
MC3 are satisfied by all complex R functions that G is dependent sively solved. A complete list of templates is available in [24].
on, then G = max o Gall . These checks are performed in the We now describe a template for solving composite recurrences
analysis for all bound functions. This includes the case when G of the form
is an R function. So, by induction, the previous argument holds c1 , . . . , cj+1 , if n = i, . . . , i + j, respectively
for all bound functions because for any called R function F , F = T (n) =
max(e1 (n), e2 (n) + aT (n − b)), otherwise
max o F all .
An example of a recurrence T whose body contains an arg-use where i, j, c1 , ..., cj+1 , a, b are constants, and e1 (n) and e2 (n) are
of a complex R function Rf by T , and which does not satisfy MC3 closed forms in n. T has j + 1 base cases and one recursive
is: case. The requirements for applicability of the template include
Rfall (m) = {m − 1, . . . , 0}, Rf (m) = m − 1 a few conditions that ensure the recurrence is well-defined, e.g.,
0 < b ≤ (j + 1). The remaining requirements define three separate
n, if m = 0 cases in which a solution can be ascertained. In case (a), we also
T (n, m) =
T (n − 1, Rf (m)), otherwise conclude that the recurrence is simple, and therefore this case is
=n−m checked first. Let E1 be e1 (n), and let E2 be e2 (n) + aT (n − b).
diff(ls1 , ls2 ) is an example of an input function from which a
bound function similar to T might be derived. diff(ls1 , ls2 ) returns Case (a): If the following conditions hold,
the tail list of ls1 of size |ls1 | − |ls2 |. A variation of diff, with A1: e1 (n) = e2 (n) + ac(n−b−i+1) , ∀n ∈ [i + j + 1, i + j + b]
similar bound functions, returns a copy of the tail list. A2: e1 (n) = e2 (n) + ae1 (n − b), ∀n > (i + j + b)
diff(ls1 , ls2 ) = if null?(ls2 ) then ls1 then T is simple, and the solution of T is:
else diff(cdr(ls1 ), cdr(ls2 ))
c1 , . . . , cj+1 , if n = i, . . . , i + j, respectively
The need for checks MC2 and MC3 stems from the use of T (n) =
e1 (n), otherwise
R function results as arguments to other bound functions. Check
MC1 simply ensures that size arguments to bound functions are, Informally, this case applies when all unfoldings of T , using all
uniformly, maximums of sizes seen during program execution, combinations of arg-exps E1 and E2 of max, yield the same
rather than an arbitrary size from amongst the possible sizes. These value. A1 ensures this for the smallest non-base cases of n (in
checks are not relevant to applications of S and N functions. Re- the range [i + j + 1, i + j + b]) for which T (n) uses a base
sults of S functions are never used as arguments to bound func- value of T ; this corresponds to the base case of the proof of case
tions. Results of N functions may sometimes be values of newsize (a). A2 performs the necessary check for the inductive case: for
arguments (of N functions). We argue that all N functions are, by n > (i + j + b), the value of E1 equals that of E2, unfolded once
construction, monotonically increasing with respect to newsize ar- using E1. For example, consider the R function Rre of remove-
guments. These arguments are used only to determine the number element in selection-sort.
of new heap cells in an argument that, for example, may be part 0, if lss = 1
of the result list. For example, for f (ls) = cons(1, cdr(ls)), the Rre (lss ) =
max(lss − 1, 1 + Rre (lss − 1)), otherwise
Case (a) applies and gives the solution Rre (lss ) = lss − 1. thogonal to MC3).
R1: c1 ≤ ... ≤ cj+1 , i.e., base cases are monotonically increas-
Case (b): If the following conditions hold,
ing.
B1: e1 (n) ≥ e2 (n) + ac(n−b−i+1) , ∀n ∈ [i + j + 1, i + j + b] R2: cj+1 ≤ e(i + j + 1) + aT (f (i + j + 1)), i.e., the largest
B2: e1 (n) ≥ e2 (n) + ae1 (n − b), ∀n > (i + j + b) base value is not greater than the value of T at the smallest
non-base case.
then T is complex, and the solution of T is: R3: ∀n > (i + j). f (n) < n. For example, this holds for
f (n) = n − b and f (n) = n/2 (integer division).
c1 , . . . , cj+1 , n = i, . . . , i + j, respectively
T (n) = R4: f is monotonically increasing w.r.t. n. For example, this
e1 (n), otherwise
holds for f (n) = n − b and f (n) = n/2.
The solution is based on the observation that, under these condi-
R5: ∀n > (i + j), e(n) > 0 and e(n) is monotonically
tions, E1 always yields a larger value than E2.
increasing w.r.t. n.
Case (c): Define T 0 as It is easy to see that if R1-R5 are satisfied by a recurrence G,
then MC3 is satisfied for G. The above template matches Rqs
c1 , . . . , cj+1 , if n = i, . . . , i + j, respectively which after evaluation of the calls to Rlls and Rgls is
T 0 (n) =
e2 (n) + aT 0 (n − b), otherwise
0, if lss = 0
Suppose the solution of T 0 is Rqs (lss ) = 1, if lss = 1
1 + 2R (ls − 1), otherwise
qs s
c1 , . . . , cj+1 , n = i, . . . , i + j, respectively
T 0 (n) =
e3 (n), otherwise
11. Examples
If e1 (n) ≤ e3 (n) for n > (i + j), then T is complex and has We applied the analysis to several list-processing programs, namely
the same solution as T 0 . The solution is based on the observation list reversal, insertion sort, selection sort, merge sort, quicksort, and
that, under these conditions, arg-exp E2 of max always yields the programs that compute longest common subsequence, string edit
maximal value. distance, and binomial coefficients efficiently using dynamic pro-
gramming. In the latter set of programs, lists (instead of arrays)
are used to store results of subproblems. Figure 5 shows the re-
10. Performing Test MC3 sults of our analysis. The closed forms derived by the analysis in-
Test MC3 is performed on bound function recurrences that contain clude linear and quadratic polynomials, and exponential formulae.
arg-uses of complex R functions, where the arg-users are the defin- A complete listing of example programs, their bound functions, re-
ing bound functions. Consider such a bound function G: currence relations, resulting closed forms, and how these were de-
rived, appears in [24].
G(v) = . . . G(F (v)) . . . The accuracy (tightness) of the analysis results was confirmed
where F is a complex R function. We consider a single-argument by comparing with results from the analysis in [25] for the same
function for simplicity. The discussion is easily extrapolated for programs. The closed form solutions derived by our analysis are
functions with multiple arguments. Test MC3 checks if using the exact maximums of live heap space usage of all example programs
result of F (v) in the definition of G yields max(Gall (v)), which except quicksort. Our analysis gives a loose bound for quicksort
is the desired result of G. because it does not recognize that the sizes of the results of func-
An example of such a bound function is Rqs in Section 7. If Rqs tions lesserorequal-list (this is the function abbreviated as lls in
does not satisfy MC3, then the analysis cannot proceed without Section 7) and greater-list (which is similar but returns the sublist
an alternate strategy, e.g., if G is an N function, then it can be containing elements greater than the cutoff) are correlated, specifi-
redefined without newsize arguments, as discussed in Section 6. cally, that when both functions are applied to the same list of length
This simplification may allow test MC3 to succeed or even make it n with the same cutoff, the sizes of their results sums to n. We
unnecessary. verified that this is the only source of inaccuracy in the analysis
Test MC3 is difficult to check in the general case. We developed of quicksort by manually modifying the relevant bound functions
templates that perform the test by matching recurrences against of quicksort to take this invariant into account; the analysis then
safe patterns and ensuring certain properties such as the following: produces a quadratic polynomial that accurately describes the heap
base values of the recurrence are monotonically increasing, base usage of quicksort.
values are less than values of the recurrence at the smallest non- Figure 4 shows how live heap space analysis works for an
base cases, and closed form expressions in the recurrence definition example program, namely insertion-sort. It lists the functions of
contain only monotonically increasing operators. Templates are insertion-sort, corresponding S, N , and R functions, recurrences
constructed such that, when checking MC3 for aforementioned G, obtained by simplifying the bound functions, and closed form so-
it is sufficient to provide its definition using just the solution of F , lutions of the recurrences.
instead of the set of elements of F all . A complete list of templates
is available in [24]. 12. Extensions to the Analysis
One of the templates that test MC3 matches recurrences of the Our analysis can be extended to data types other than lists by defin-
form ing an appropriate R function for each data type and introducing
corresponding size arguments to bound functions. Some data types,
c1 , ..., cj+1 , n = i, ..., i + j, respectively
T (n) = such as arrays, are similar to lists and can be handled easily. Binary
e(n) + aT (f (n)), otherwise
trees are more interesting. Their size can be measured by height and
where i, j, c1 , ..., cj+1 , a are constants, and e(n) is a closed form number of nodes, so we define two R functions for binary trees,
in n. The given recurrence is required to meet the following condi- Rheight and Rnodes , and for each binary-tree argument v of each
tions (we elide general well-formedness requirements that are or- function f in the original program, we introduce arguments vh and
Derived Heap
Examples Entry Function S Function of Entry Function Tight?
Usage Complexity
0 if lss = 0
reverse rev(ls) Srev (lss ) = Linear Yes
2lss − 1, otherwise
0 if lss = 0
insertion sort is(ls) Sis (lss ) = Linear Yes
2lss − 1, otherwise
lss (lss +1)
selection sort ss(ls) Sss (lss ) = 2
Quadratic Yes
0 if lss = 0
merge sort ms(ls) Sms (lss ) = Linear Yes
2lss − 1, otherwise
0 if lss = 0, 1
quick sort qs(ls) Sqs (lss ) = Exponential No
3.2lss −1 − 2, otherwise
1 if ls1s = 0 or ls2s = 0
longest common subsequence lcs(ls1 , ls2 ) Slcs (ls1s , ls2s ) = ls2s + 2 if ls1s = 1 Linear Yes
2ls + 2, otherwise
2s
str1s + str2s + 1,
string edit distance se(str1 , str2 ) Sse (str1s , str2s ) = if str1s = 0 or str2s = 0 Linear Yes
2str + 2str2s + 1, otherwise
1s
1 if ms = ns
binomial coefficient bc(n, m) Sbc (ns , ms ) = ms + 2 if ms = ns − 1 Linear Yes
2m + 2, otherwise
s
Figure 5. Parametric heap space usage bounds derived for some example programs.
vn to the bound functions of f , representing the height and number ing program execution, while time always increases. Further, live
of nodes, respectively, of the tree passed to v. heap space analysis needs to determine when allocations become
In the presence of these new data types, bound functions may garbage.
need to make approximations, e.g., that each branch of a binary Wegbreit [26] proposed deriving closed form expressions de-
tree v has at most min(2vh − 1, vn − 1) nodes. We can mitigate, if scribing running times of Lisp programs in terms of sizes of inputs,
not eliminate, the effects of such approximations by using type- much like we do for live heap space, by deriving recursive functions
specific invariants in the analysis, e.g., vn ≤ 2vh +1 − 1 and describing running times, converting them into difference equa-
Rnodes [left(e)]] + Rnodes [right(e)]] = Rnodes [e]], where left(e) tions and solving the same. Analyses that produce such formulas
and right(e) return the subtrees of e, respectively. Note that the are sometimes called parametric analyses. Probabilities attached to
metric Rnodes is useful for all data types, including nested lists. ambiguous predicates (that require information other than sizes of
The current R functions are, in fact, Rlength functions; for a nested inputs) enable the derivation of mean running times, in addition to
list ls, Rlength describes the length of the top-level list of ls. best-case and worst-case running times. The prototype implemen-
S and N functions, and the S and N transformations, also tation is limited to simple Lisp programs. [19] is similar to [26]
need to be specialized to new data types. For some data types, but derives asymptotic, rather than exact, upper bounds on running
S and N functions can take any one of several size measures times; also, the user is required to aid the analysis in converting the
as arguments, depending on which size measure yields the most recursive function into a closed form expression. These two papers
compact and accurate formulas representing live heap usage and describe only limited methods to handle their respective equiva-
new result-space usage, respectively. The analysis may try different lents of composite recurrences, and neither of them use the notion
size measures and then determine which yields the best results. of monotonicity requirements.
The current version of our analysis does not allow mutual recur- Most of the work related to analysis of space is on analysis of
sion in the input programs. Such mutual recursion typically leads cache behavior, e.g., [27, 14], much of which is at a lower language
to mutual recursion in the bound functions. Mutual recursion can level, for compiler generated code, while our analyses are at source
sometimes be handled by converting it into self-recursion (i.e., or- level and can serve many purposes. Live heap analysis is a first
dinary recursion): if f and g are mutually recursive, and unfolding step towards analyzing cache behavior in the presence of garbage
f a few times yields a definition of f that recursively calls only collection.
f and not g, then f is in fact self-recursive. Our analysis can be Several type systems [18, 17, 12, 11] have been proposed for
extended to first use such unfolding to try to convert mutual re- reasoning about space and time bounds, and some of them include
cursion into self-recursion. If this does not succeed, then standard implementations of type checkers [18, 12]. They require program-
mathematical techniques for solving mutual recurrences are used, mers to annotate their programs with cost functions as types. Fur-
if applicable. thermore, some programs must be rewritten to have feasible types
[18, 17]. [9] proposes a loop-detecting algorithm to identify meth-
ods and instructions that execute an unbounded number of times,
13. Related Work thereby detecting possibly unbounded memory usage. [4, 7] ensure
There is a large amount of work on analyzing program cost or re- acceptable memory use in bytecode, by verifying specified resource
source complexities, but the majority of it is on time analysis, e.g., annotations or memory consumption policies.
[26, 19, 22, 23, 20, 15]. Analysis of stack space and heap alloca- [16] proposes a method to obtain linear bounds on heap space
tion are similar to time analysis [20]. Analysis of live heap space consumption of functional programs using type derivations and
is different because live heap space increases and decreases dur- linear programming. While programs are not required to be linearly
typed, restrictions are made on sharing of heap cells. Our analysis the first call to g(n) become garbage before the second call to g(n).
is not restricted to linear bounds and does not restrict sharing of The method of [1, 2] does not recognize this (since cells allocated
heap cells. in g escape it) and yields a bound of 2n on the space usage of f .
Several methods have been developed to automatically infer Albert et al.’s paper in the same proceedings analyzes live heap
heap space bounds for Java-like imperative languages and byte- space (as opposed to total heap allocation and active heap space)
code. Imperative update poses special challenges for determining in the presence of garbage collection [3]. The methods used in our
liveness. Chin et al. [10] propose a method to infer stack and heap analysis to solve composite recurrences can be enhanced by the
usage bounds for an assembly-like language. “dispose” commands techniques in [1] to solve non-deterministic cost relations and those
that reclaim unused heap space, are explicitly inserted into input in [15] to handle disjunctive invariants.
programs [11] at points where objects are no longer live. Recur-
sive constraint abstractions derived from these modified input pro-
grams describe stack and heap usages of the programs. Fixpoint 14. Conclusions and Future Work
analysis of constraint abstractions using a Presburger solver yields In summary, this paper describes a method that aims to determine
closed form expressions for stack and heap usages. Our analy- worst-case live heap space usage of functional programs automati-
sis applies to a high-level language and is not limited to Pres- cally and accurately using source-level program analysis and trans-
burger formulae, so it can derive both linear and non-linear bounds. formations. Specific technical contributions include a framework
The alias/uniqueness analysis used to determine liveness and insert for deriving bound (S, N , and R) functions that describe the live
“dispose” commands appropriately, is an over-approximation of the heap space usage of functional programs, the identification and for-
behaviour of an ideal garbage collector. In the absence of impera- mulation of conditions (tests MC1, MC2, and MC3) that are nec-
tive update, live heap analysis is able to determine liveness exactly. essary to ensure soundness of the analysis, and the templates used
As a result, our bounds on heap usage are tighter than those of [10]. to solve recurrences and check part of the monotonicity condition
While their analysis has been applied to several benchmarks, all ex- (namely, MC3). The results reported in Section 11 suggest that the
cept one of the benchmarks use few heap objects. analysis usually produces tight bounds in practice. Although these
The heap analysis in [8] is also targeted to an imperative lan- example programs are small, they have non-trivial patterns of heap
guage but is able to derive polynomial, not just linear, heap bounds. allocation and garbage collection. In part, this is because they are
Our analysis is not limited to a complexity category, although it is functional programs and must allocate new data structures instead
limited by the solvability of the encountered recurrences. Region- of updating existing data structures.
based memory management is used to model garbage collection; Future work on live heap analysis for functional programs in-
objects are allocated in regions associated with methods and re- cludes modifying the analysis to reflect the effect of optimization of
gions are garbage collected at the end of methods. As in the above tail recursion, incorporating techniques for analysis of higher-order
discussion, this is an over-approximation of heap usage compared functions [23, 20], and evaluating the accuracy and scalability of
to our analysis, which is, in the absence of imperative update, able the analysis more extensively. Another important direction for fu-
to model “ideal” garbage collection (objects are garbage collected ture work is to modify the analysis to handle imperative update.
as soon as they become dead). The maximum memory occupied by
a region configuration is modelled as a parametric polynomial op-
timization problem. This is then solved using Bernstein basis. The References
analysis does not handle recursions.
[1] E. Albert, P. Arenas, S. Genaim, and G. Puebla. Automatic inference
Albert et al. propose a two-part analysis of heap space usage for
of upper bounds for recurrence relations in cost analysis. In Proc.
Java bytecode [2]. The first part constructs cost equations character- 15th International Static Analysis Symposium (SAS 2008), pages
izing the amount of heap space allocated by each method and then 221–237, 2008.
uses the technique in [1] to obtain closed-form upper bounds on the
[2] E. Albert, S. Genaim, and M. Gómez-Zamalloa. Heap space analysis
total amount of allocation. The second part takes garbage collec-
for java bytecode. In Proc. 6th International Symposium on Memory
tion into account in a simple way by using escape analysis to iden- Management (ISMM 2007), pages 105–116, 2007.
tify allocations that do not escape from the static scope containing
the allocation statement, and then ignoring these allocations when [3] E. Albert, S. Genaim, and M. Gomez-Zamalloa. Live heap space
analysis for languages with garbage collection. In Proc. 7th
computing the total amount of heap allocation. Solving the result-
International Symposium on Memory Management (ISMM ’09),
ing cost equations provides an upper bound on what they call the 2009.
active heap space for each method. On the one hand, their analysis
handles various language features that ours does not, most notably [4] D. Aspinall, S. Gilmore, M. Hofmann, D. Sannella, and I. Stark.
Mobile resource guarantees for smart devices. In Proceedings of the
imperative update. On the other hand, their analysis cannot easily
International Workshop on Construction and Analysis of Safe, Secure
be extended to compute what our analysis computes—the maxi- and Interoperable Smart Devices (CASSIS), volume 3362 of LNCS,
mum live heap space, which they call the memory high-watermark. pages 1–26. Springer, 2005.
The “active heap space” computed by their analysis only increases
[5] D. F. Bacon. Realtime garbage collection. Queue, 5(1):40–49, 2007.
(like running time); live heap space is different, because it increases
and decreases. They write: “Analysis for finding upper bounds on [6] R. Bagnara, A. Pescetti, A. Zaccagnini, E. Zaffanella, and T. Zolo.
the memory high-watermark cannot be directly done using cost re- PURRS: The Parma University’s recurrence relation solver. http:
lations as introducing decrements in the equations requires com- //www.cs.unipr.it/purrs.
puting lower bounds” [2, Section 6]. To illustrate the difference [7] G. Barthe, M. Pavlova, and G. Schneider. Precise analysis of memory
between live space and “active heap space”, consider the program consumption using program logics. In SEFM ’05: Proceedings of the
Third IEEE International Conference on Software Engineering and
f (n) = len(g(n)) + len(g(n)) Formal Methods, pages 86–95, Washington, DC, USA, 2005. IEEE
g(n) = if n = 0 then nil else cons(n, g(n − 1)) Computer Society.
len(l) = if null?(l) then 0 else 1 + len(cdr(l)) [8] V. Braberman, F. Fernández, D. Garbervetsky, and S. Yovine.
Parametric prediction of heap memory requirements. In ISMM
Our analysis yields Sf (ns ) = ns , i.e., the maximum live heap ’08: Proceedings of the 7th international symposium on Memory
space used by f is n cons cells, reflecting that the cells allocated by management, pages 141–150, New York, NY, USA, 2008. ACM.
[9] D. Cachera, T. Jensen, D. Pichardie, and G. Schneider. Certified [20] Y. A. Liu and G. Gómez. Automatic accurate time-bound analysis
memory usage analysis. In Formal Methods 05, volume 3582 of for high-level languages. In Proceedings of the ACM SIGPLAN
LNCS, pages 91–106. Springer-Verlag, 2005. 1998 Workshop on Languages, Compilers, and Tools for Embedded
[10] W.-N. Chin, H. H. Nguyen, C. Popeea, and S. Qin. Analysing memory Systems, volume 1474 of Lecture Notes in Computer Science, pages
31–40. Springer-Verlag, 1998.
resource bounds for low-level programs. In ISMM ’08: Proceedings
of the 7th international symposium on Memory management, pages [21] M. B. Monagan, K. O. Geddes, K. M. Heal, G. Labahn, S. M.
141–150, New York, NY, USA, 2008. ACM. Vorkoetter, J. McCarron, and P. DeMarco. Maple 10 Programming
[11] W.-N. Chin, H. H. Nguyen, S. Qin, and M. Rinard. Memory usage Guide. Maplesoft, 2005.
verification for oo programs. In SAS 05, pages 70–86. Springer, 2005. [22] M. Rosendahl. Automatic complexity analysis. In Proceedings of the
4th International Conference on Functional Programming Languages
[12] K. Crary and S. Weirich. Resource bound certification. In Conference
Record of the 27th Annual ACM Symposium on Principles of and Computer Architecture, pages 144–156. ACM Press, Sept. 1989.
Programming Languages. ACM Press, 2000. [23] D. Sands. Complexity analysis for a lazy higher-order language. In
[13] W. Fu and C. Hauser. A real-time garbage collection framework N. D. Jones, editor, Proceedings of the 3rd European Symposium on
for embedded systems. In SCOPES ’05: Proceedings of the 2005 Programming, volume 432 of Lecture Notes in Computer Science,
pages 361–376. Springer-Verlag, Berlin, May 1990.
workshop on Software and compilers for embedded systems, pages
20–26, New York, NY, USA, 2005. ACM. [24] L. Unnikrishnan. Automatic Live Memory Bound Analysis for
[14] S. Ghosh, M. Martonosi, and S. Malik. Cache miss equations: A High-Level Languages. PhD thesis, Stony Brook University, 2008.
compiler framework for analyzing and tuning memory behavior. Available at http://www.cs.sunysb.edu/~leena/thesis.pdf.
ACM Trans. Program. Lang. Syst., 21(4):703–746, July 1999. [25] L. Unnikrishnan, S. D. Stoller, and Y. A. Liu. Optimized live heap
bound analysis. In Proceedings of the 4th International Conference
[15] S. Gulwani, K. K. Mehra, and T. M. Chilimbi. Speed: precise and
efficient static estimation of program computational complexity. on Verification, Model Checking and Abstract Interpretation, Jan.
In Proc. 36th Annual Symposium on Principles of Programming 2003.
Languages (POPL 2009), 2009. [26] B. Wegbreit. Mechanical program analysis. Communications of the
[16] M. Hofmann and S. Jost. Static prediction of heap space usage for ACM, 18(9):528–538, Sept. 1975.
first-order functional programs. In POPL03 Symposium on Principles [27] R. Wilhelm and C. Ferdinand. On predicting data cache behaviour
of Programming Languages, pages 185–197. ACM Press, 2003. for real-time systems. In Proceedings of the ACM SIGPLAN
[17] J. Hughes and L. Pareto. Recursion and dynamic data-structures 1998 Workshop on Languages, Compilers, and Tools for Embedded
Systems, volume 1474 of Lecture Notes in Computer Science, pages
in bounded space: Towards embedded ML programming. In
Proceedings of the 1999 ACM SIGPLAN International Conference on 16–30. Springer-Verlag, 1998.
Functional Programming, pages 70–81. ACM Press, Sept. 1999.
[18] J. Hughes, L. Pareto, and A. Sabry. Proving the correctness of reactive
systems using sized types. In Conference Record of the 23rd Annual
ACM Symposium on Principles of Programming Languages, pages
410–423. ACM Press, 1996.
[19] D. Le Métayer. Ace: An automatic complexity evaluator. ACM Trans.
Program. Lang. Syst., 10(2):248–266, Apr. 1988.