<div class="gmail_quote">On Fri, Apr 23, 2010 at 17:29, Matthew Flatt <span dir="ltr"><<a href="mailto:mflatt@cs.utah.edu">mflatt@cs.utah.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<div class="im">At Thu, 22 Apr 2010 20:28:55 -0400, "Skeptic ." wrote:<br>
> I'm curious to know how in PLT what kind of performance difference<br>
> (in the broad sense) would be found when comparing a tree-recursive<br>
> procedure to a tail-recursive one that use an explicit stack and a<br>
> non-trivial nested list, e.g. flatten, count-atoms and the like.<br>
<br>
</div>It's generally better to write in direct, tree-recursive style.<br>
<br>
For example, many systems implement `map' in a tail-recursive way to<br>
avoid deep recursion:<br>
<br>
(define (map1/loop f l)<br>
(let loop ([l l][accum null])<br>
(if (null? l)<br>
(reverse accum)<br>
(loop (cdr l) (cons (f (car l)) accum)))))<br>
<br>
The single-list `map' in PLT Scheme, however, is implemented in direct<br>
style:<br>
<br>
(define (map1 f l)<br>
(if (null? l)<br>
null<br>
(cons (f (car l)) (map1 f (cdr l)))))<br>
<br>
The direct style usually performs better in PLT Scheme, and we don't<br>
have to worry about a special limit on continuation sizes (i.e., no<br>
stack overflow).<br>
<br>
The direct style performs better in the `map' case because the GC can<br>
process continuation frames more efficiently than `cons' chains. That<br>
isn't typical across implementations; the GC in many Scheme systems is<br>
especially highly tuned for pairs, and less so for continuation frames.<br>
<br>
<br>
Of course, if you want to just count the elements of a list, then a<br>
loop version<br>
<br>
(define (count/tail l)<br>
(let loop ([l l][accum 0])<br>
(if (null? l)<br>
accum<br>
(loop (cdr l) (add1 accum)))))<br>
<br>
is 10 times faster than a direct version<br>
<br>
(define (count l)<br>
(if (null? l)<br>
0<br>
(+ 1 (count (cdr l)))))<br>
<br>
The difference is that the number result can be accumulated<br>
left-to-right and doesn't have to be "reversed" at the end. For<br>
performance, not having a continuation at all is better than having<br>
one.<br>
<br>
<br>
Those examples were not tree recursion, but they point to a general<br>
answer: If your computation has a continuation, let the runtime system<br>
represent it. Your program will be easier to write and read in direct<br>
style, and it will probably perform as well (if not better) as with an<br>
explicit representation.<br>
<div><div></div><br></div></blockquote></div><br>While programming a cellular automaton, I first used `foldl' to do some
computation on the list of neighbors.<br>
That was awfully slow, so I tried with a hand-written loop, and it
became much faster -- fast enough for fluid visual effect.<br>
I'm not sure exactly why `foldl' was so slow though...<br><br>