caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: skaller <skaller@users.sourceforge.net>
To: Jonathan Roewen <jonathan.roewen@gmail.com>
Cc: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] HOFs, recursion, and being tail-rec...
Date: Mon, 13 Feb 2006 14:23:45 +1100	[thread overview]
Message-ID: <1139801025.8591.137.camel@rosella.wigram> (raw)
In-Reply-To: <ad8cfe7e0602121847q53985635v4af48705831e2aa3@mail.gmail.com>

On Mon, 2006-02-13 at 15:47 +1300, Jonathan Roewen wrote:
> > So it isn't a well formed sentence to say depth first
> > search cannot be tail-rec, it is easy to construct a
> > depth first search in any FPL that is.
> >
> > However, no matter what you do, you will indeed need
> > memory linear in the tree depth (unless it is 1-ary tree as
> > pointed out).
> 
> Yes, but tail-rec would mean the function call trace does not use
> memory linear in the tree depth, which would be a positive
> optimisation. 

I do not think you can assume that without measuring it,
either by an actual speed test, or examining the generated
machine code.

> BTW: the memory linear to the tree depth is used in the
> list passed to search, 'visited'.

Yes.

> I'm just wondering if using List.exists and HOFs prevents the compiler
> from generating tail-rec code (I presume that List.exists is already
> tail-rec). 

I am not an expert on Ocaml optimisation -- but I would
guess your code does indeed cause the machine stack
to be pushed with the return address of 'search':

let rec search visited position =
      if position = goal then true
      else 
	List.exists 
	(search (position::visited)) 
	(positions position edges (position::visited))

Here 'search' is called to calculate an argument to List.exists,
which is the last call, and the one in tail position. 

Well, actually this is not correct! The function in tail
position *in theory* is actually

	(List.exists (search (position::visited)))

since application is left associative. However Ocaml is
smart and I believe it knows how to make curried calls
of explicitly named functions without closure construction.
In other words, if possible, it calls

	let f x y = .. in f a b (* f is NOT in tail position *)

as if you had written

	let f (x,y) = .. in f (x,y) (* f IS in tail position *)

This just shows that 'tail-rec' is a muddled idea. It is a purely
syntactic property, when what you're interested in is performance.
The correlation is implementation dependent.

In any case, 'search' call in your code is NOT in tail position
within the function search any way you look at it. I'd be surprised
if Ocaml could optimise the code above to eliminate recursive
calls.

There IS a way to do this in some cases that I know about,
which I hope to implement in Felix one day: a significant
class of non-tail recursive functions can in fact be
implemented without subroutine calling. I think your
code is an example of this.

> As it wouldn't be hard to make it tail-rec I'd imagine..

Generally KISS is a good idea for two reasons which
are the same reason:

(a) you can reason about your code more easily
(b) the compiler optimiser can reason about your code more easily

In the case of a balanced tree there is no reason to worry
about recursion. The formula for the size of the tree is
exponential in the tree depth, so a couple of recursions
on a linear memory stack is irrelevent compared to the
disk paging you'd get thrashing about trying to search
a tree of any significant depth.

As a counter example, I think it was Jacques who actually
found Felix lexer was scanning a list of tokens non-tail
recursively, which had a significant impact on Felix
performance -- causing stack overflow lexing larger
files. That's the degenerate case of an 1-ary tree :)


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


      reply	other threads:[~2006-02-13  3:23 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-02-12 22:08 Jonathan Roewen
2006-02-12 23:53 ` Jacques Garrigue
2006-02-13  2:05   ` skaller
2006-02-13  2:47     ` Jonathan Roewen
2006-02-13  3:23       ` skaller [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1139801025.8591.137.camel@rosella.wigram \
    --to=skaller@users.sourceforge.net \
    --cc=caml-list@yquem.inria.fr \
    --cc=jonathan.roewen@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).