efficiency, deep lists
Richard Carlsson
richardc@REDACTED
Tue Oct 2 15:57:33 CEST 2001
On 2 Oct 2001, Luke Gorrie wrote:
> Luke Gorrie <luke@REDACTED> writes:
> For benchmarking, here's the list-of-lists version, which is just
> Ulf's incr2 with the recursive call moved from where the continuation
> is created to where the continuation is invoked:
This was interesting, so I did some benchmarks of my own. The results
seem to have little variance, and I think they can be trusted:
Time GC Reclaimed
incr1: 11100 40000 2.1e7 ;naive
incr2: 7200 25005 1.5e7 ;fun-continuation
incr3: 6800 10014 2.0e7 ;bad foldl-version
incr3': 11900 44000 4.7e7 ;corrected foldl-version
incr4: 7400 5003 2.1e7 ;flatten first
incr5: 9000 30001 1.1e7 ;continuation as data
incr6: 5200 10002 1.0e7 ;list-of-lists only
incr7: 9200 53342 2.4e7 ;tail recursive version of 5
incr8: 7600 26668 2.3e7 ;tail recursive version of 6
incr9: 8000 20738 1.6e7 ;fun-continuation (any depth)
Explanations: incr3 was incorrectly implemented - the returned list is
in the wrong order, and since it did not compute the same function, it
could execute more efficiently than incr1, even though they seem to be
very similar at first glance. The corrected incr3' looks as follows:
incr3(List) ->
lists:reverse(lists:foldl(
fun(L, Acc) ->
lists:reverse(incr11(L)) ++ Acc
end, [], List)).
and as can be seen above, it is comparable to incr1.
incr5 and incr6 are those implemented by Luke. As the table shows, incr6
is clearly the fastest implementation, but note that incr2 is not bad in
comparison to incr1 and incr4. incr7 and incr8 are tail-recursive
versions of Luke's implementations, using an accumulator argument and
reversing the final list before returning. The measurements show that
there is apparently no advantage to that technique in this case.
The really interesting version is incr9, which both uses funs as
continuations and handles lists of any depth. The code is very elegant,
and as you can see from the table, it is actually more efficient than
incr5.
/Richard
Code for incr7-9:
incr7(L) -> lists:reverse(incr77(L, [], [])).
%% incr77(DeepList, [Continuation], [Accumulator])
incr77([H|T], C, A) when list(H) ->
incr77(H, [T|C], A);
incr77([H|T], C, A) ->
incr77(T, C, [H+1|A]);
incr77([], [C|Cs], A) ->
incr77(C, Cs, A);
incr77([], [], A) ->
A
incr8(L) -> lists:reverse(incr88(L, [])).
incr88([H|T], A) when list(H) ->
incr888(H, T, A);
incr88([], A) ->
A.
incr888([H|T], C, A) ->
incr888(T, C, [H+1|A]);
incr888([], C, A) ->
incr88(C, A).
incr9(L) -> incr99(L, fun () -> [] end).
incr99([H|T], C) when list(H) ->
incr99(H, fun () -> incr99(T, C) end);
incr99([H|T], C) ->
[H+1|incr99(T, C)];
incr99([], C) ->
C().
Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.)
E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/
More information about the erlang-questions
mailing list