<p>Probably because the recursions over lists have been already optimized by the compiler, there is almost no difference between body-recursion and tail-recursion.</p>
<div class="gmail_quote">On Nov 18, 2011 11:29 PM, "Dmitry Demeshchuk" <<a href="mailto:demeshchuk@gmail.com">demeshchuk@gmail.com</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
If it's garbage collected on your system, try to decrease the lists<br>
size so GC won't be run automatically. On my laptop, as you can see,<br>
each process eats about 40MB of RAM, which seems to be more or less<br>
correct.<br>
<br>
On Fri, Nov 18, 2011 at 7:08 PM, Barco You <<a href="mailto:barcojie@gmail.com">barcojie@gmail.com</a>> wrote:<br>
> Seemingly our methods are all not correct for measuring memory consumption.<br>
> If measuring in the middle as I did, we just get the instant value at a<br>
> moment, whereas if measuring after the function returns by waiting for<br>
> several seconds as you did, all the memory ever used have been garbage<br>
> collected. So I think what we need to do for profiling the functions is to<br>
> draw a curve with memory consumption against time elapsed. But I dont know<br>
> how to do that.<br>
><br>
> On Nov 18, 2011 5:41 PM, "Dmitry Demeshchuk" <<a href="mailto:demeshchuk@gmail.com">demeshchuk@gmail.com</a>> wrote:<br>
>><br>
>> That's not the correct way. You were measuring the processes memory in<br>
>> the middle of calculations.<br>
>><br>
>> 1> A = spawn(fun () -> test:a(), receive _ -> ok end end).<br>
>> <0.46.0><br>
>> 2> B = spawn(fun () -> test:b(), receive _ -> ok end end).<br>
>> <0.48.0><br>
>> 3> process_info(A, memory).<br>
>> {memory,391814792}<br>
>> 4> process_info(B, memory).<br>
>> {memory,401610112}<br>
>><br>
>> Note that I waited for several seconds before entering commands 3 and 4.<br>
>><br>
>> On Fri, Nov 18, 2011 at 1:32 PM, Barco You <<a href="mailto:barcojie@gmail.com">barcojie@gmail.com</a>> wrote:<br>
>> > Very strange! By using another measuring method as below I got that your<br>
>> > function is far better than mine in memory consumption.<br>
>> ><br>
>> > -module(test).<br>
>> > -export([a/0, b/0]).<br>
>> > a() -><br>
>> >         L = lists:seq(1, 10000000),<br>
>> >         map2(fun (I, J) -> I + J end, L, L),<br>
>> >         receive<br>
>> >                 stop -><br>
>> >                         ok<br>
>> >         end.<br>
>> > b() -><br>
>> >         L = lists:seq(1, 10000000),<br>
>> >         map3(fun (I, J) -> I + J end, L, L),<br>
>> >         receive<br>
>> >                 stop -><br>
>> >                         ok<br>
>> >         end.<br>
>> ><br>
>> > map2(Fun, [H1 | T1], [H2 | T2]) -><br>
>> >         [Fun(H1, H2) | map2(Fun, T1, T2)];<br>
>> > map2(_, [], []) -><br>
>> >         [].<br>
>> > map3(_Fun, [], []) -><br>
>> >         [];<br>
>> > map3(Fun, L1, L2) -><br>
>> >         map3(Fun, L1, L2, []).<br>
>> > map3(_Fun, [], [], L) -><br>
>> >         lists:reverse(L);<br>
>> > map3(Fun, [H1 | T1], [H2 | T2], L) -><br>
>> >         map3(Fun, T1, T2, [Fun(H1, H2) | L]).<br>
>> ><br>
>> > Eshell V5.8.4  (abort with ^G)<br>
>> > 1> A = spawn(test,a,[]).<br>
>> > <0.32.0><br>
>> > 2> B = spawn(test,b,[]).<br>
>> > <0.34.0><br>
>> > 3> erlang:process_info(A,memory).<br>
>> > {memory,176316700}<br>
>> > 4> erlang:process_info(B,memory).<br>
>> > {memory,306105040}<br>
>> ><br>
>> ><br>
>> ><br>
>> > On Fri, Nov 18, 2011 at 4:53 PM, Dmitry Demeshchuk<br>
>> > <<a href="mailto:demeshchuk@gmail.com">demeshchuk@gmail.com</a>><br>
>> > wrote:<br>
>> >><br>
>> >> I'm not sure it is so. Try running this function several times:<br>
>> >><br>
>> >> memtest() -><br>
>> >>    erlang:garbage_collect(),<br>
>> >>    M1 = proplists:get_value(total, erlang:memory()),<br>
>> >>    a(),<br>
>> >>    M2 = proplists:get_value(total, erlang:memory()),<br>
>> >>    b(),<br>
>> >>    M3 = proplists:get_value(total, erlang:memory()),<br>
>> >>    {M2 - M1, M3 - M2}.<br>
>> >><br>
>> >> The results are pretty the same.<br>
>> >><br>
>> >> On Fri, Nov 18, 2011 at 12:36 PM, Barco You <<a href="mailto:barcojie@gmail.com">barcojie@gmail.com</a>> wrote:<br>
>> >> > Umm! I don't think there would be some difference between times<br>
>> >> > consumed<br>
>> >> > by<br>
>> >> > these two functions, but I will assume there are difference in memory<br>
>> >> > consumption.<br>
>> >> ><br>
>> >> ><br>
>> >> > On Fri, Nov 18, 2011 at 3:49 PM, Dmitry Demeshchuk<br>
>> >> > <<a href="mailto:demeshchuk@gmail.com">demeshchuk@gmail.com</a>><br>
>> >> > wrote:<br>
>> >> >><br>
>> >> >> I think it's meant that lists:reverse/1 is called at the end of the<br>
>> >> >> _optimized_ code.<br>
>> >> >><br>
>> >> >> Here's the module code:<br>
>> >> >><br>
>> >> >> -module(test).<br>
>> >> >> -export([a/0, b/0]).<br>
>> >> >><br>
>> >> >> a() -><br>
>> >> >>    L = lists:seq(1, 10000000),    map2(fun (I, J) -> I + J end, L,<br>
>> >> >> L).<br>
>> >> >><br>
>> >> >> b() -><br>
>> >> >>    L = lists:seq(1, 10000000),<br>
>> >> >>    map3(fun (I, J) -> I + J end, L, L).<br>
>> >> >><br>
>> >> >><br>
>> >> >> map2(Fun, [H1 | T1], [H2 | T2]) -><br>
>> >> >>    [Fun(H1, H2) | map2(Fun, T1, T2)];<br>
>> >> >> map2(_, [], []) -><br>
>> >> >>    [].<br>
>> >> >><br>
>> >> >> map3(_Fun, [], []) -><br>
>> >> >>   [];<br>
>> >> >> map3(Fun, L1, L2) -><br>
>> >> >>   map3(Fun, L1, L2, []).<br>
>> >> >><br>
>> >> >> map3(_Fun, [], [], L) -><br>
>> >> >>   lists:reverse(L);<br>
>> >> >> map3(Fun, [H1 | T1], [H2 | T2], L) -><br>
>> >> >>   map3(Fun, T1, T2, [Fun(H1, H2) | L])<br>
>> >> >><br>
>> >> >> Try to call timer:tc/3 yourself.<br>
>> >> >><br>
>> >> >> On Fri, Nov 18, 2011 at 11:48 AM, Barco You <<a href="mailto:barcojie@gmail.com">barcojie@gmail.com</a>><br>
>> >> >> wrote:<br>
>> >> >> > According to the instruction attached by Ulf, the body-recursive<br>
>> >> >> > and<br>
>> >> >> > tail-recursive list function will be the same in consuming memory<br>
>> >> >> > only<br>
>> >> >> > when<br>
>> >> >> > they call lists:reverse/1 at the end.<br>
>> >> >> > So, I don't know how did you do the benchmarks. Did you compare<br>
>> >> >> > these<br>
>> >> >> > two<br>
>> >> >> > methods with big enough lists?<br>
>> >> >> > Or, I misunderstand the optimization instructions?<br>
>> >> >> ><br>
>> >> >> > BR,<br>
>> >> >> > Barco<br>
>> >> >> ><br>
>> >> >> > On Fri, Nov 18, 2011 at 3:37 PM, Dmitry Demeshchuk<br>
>> >> >> > <<a href="mailto:demeshchuk@gmail.com">demeshchuk@gmail.com</a>><br>
>> >> >> > wrote:<br>
>> >> >> >><br>
>> >> >> >> Okay, I admit, this isn't an "honest" tail-recursed function,<br>
>> >> >> >> since<br>
>> >> >> >> a<br>
>> >> >> >> list concatenation operator is going to be called at the end.<br>
>> >> >> >> However,<br>
>> >> >> >> Erlang compiler optimizes such cases and converts them to<br>
>> >> >> >> tail-recursive:<br>
>> >> >> >><br>
>> >> >> >> <a href="http://www.erlang.org/doc/efficiency_guide/myths.html#tail_recursive" target="_blank">http://www.erlang.org/doc/efficiency_guide/myths.html#tail_recursive</a><br>
>> >> >> >><br>
>> >> >> >> Also, I've ran benchmarks with both implementations: mine and<br>
>> >> >> >> yours.<br>
>> >> >> >> And they result in the same performance.<br>
>> >> >> >><br>
>> >> >> >> On Fri, Nov 18, 2011 at 11:06 AM, Barco You <<a href="mailto:barcojie@gmail.com">barcojie@gmail.com</a>><br>
>> >> >> >> wrote:<br>
>> >> >> >> > Yes, Ryan's suggestion is a good generic solution for n lists<br>
>> >> >> >> > and<br>
>> >> >> >> > it's<br>
>> >> >> >> > tail-recursed.<br>
>> >> >> >> > Hi Dmitry,<br>
>> >> >> >> > Your version is just recursed but not tail-recursed, because<br>
>> >> >> >> > your<br>
>> >> >> >> > function<br>
>> >> >> >> > needs a piece of memory to stack the intermediate result for<br>
>> >> >> >> > every<br>
>> >> >> >> > round<br>
>> >> >> >> > of<br>
>> >> >> >> > recursive calls. To be tail-recursed, the recursive calls<br>
>> >> >> >> > should<br>
>> >> >> >> > eliminate<br>
>> >> >> >> > the linearly-increased memory consumption by adding an extra<br>
>> >> >> >> > variable<br>
>> >> >> >> > (accumulator) and let the recursive function call it alone for<br>
>> >> >> >> > every<br>
>> >> >> >> > round.<br>
>> >> >> >> ><br>
>> >> >> >> > On Fri, Nov 18, 2011 at 2:53 PM, Dmitry Demeshchuk<br>
>> >> >> >> > <<a href="mailto:demeshchuk@gmail.com">demeshchuk@gmail.com</a>><br>
>> >> >> >> > wrote:<br>
>> >> >> >> >><br>
>> >> >> >> >> Hi, Barco.<br>
>> >> >> >> >><br>
>> >> >> >> >> Why do you think my version isn't tail-recursed? :) Take a<br>
>> >> >> >> >> look<br>
>> >> >> >> >> at<br>
>> >> >> >> >> lists:map/2 implementation, for example. It's just the same.<br>
>> >> >> >> >><br>
>> >> >> >> >> List comprehensions just serve different purpose: for<br>
>> >> >> >> >> combinations<br>
>> >> >> >> >> from multiple list sources. My guess is that people need this<br>
>> >> >> >> >> operation more often than mapping over multiple list. Another<br>
>> >> >> >> >> problem<br>
>> >> >> >> >> is that you should be sure that all those lists have the same<br>
>> >> >> >> >> length.<br>
>> >> >> >> >><br>
>> >> >> >> >> On Fri, Nov 18, 2011 at 10:38 AM, Barco You<br>
>> >> >> >> >> <<a href="mailto:barcojie@gmail.com">barcojie@gmail.com</a>><br>
>> >> >> >> >> wrote:<br>
>> >> >> >> >> > Hi Dmitry,<br>
>> >> >> >> >> > What your suggested can really solve my problem, but it's<br>
>> >> >> >> >> > not<br>
>> >> >> >> >> > Tail-Recursion. The tail-recursed solution should look like<br>
>> >> >> >> >> > this;<br>
>> >> >> >> >> > map2(_Fun, [], []) -><br>
>> >> >> >> >> >    [];<br>
>> >> >> >> >> > map2(Fun, L1, L2) -><br>
>> >> >> >> >> >    map2(Fun, L1, L2, []).<br>
>> >> >> >> >> > map2(_Fun, [], [], L) -><br>
>> >> >> >> >> >    lists:reverse(L);<br>
>> >> >> >> >> > map2(Fun, [H1 | T1], [H2 | T2], L) -><br>
>> >> >> >> >> >    map2(Fun, T1, T2, [Fun(H1, H2) | L]).<br>
>> >> >> >> >> ><br>
>> >> >> >> >> > However, I'm still disappointed with the list comprehension<br>
>> >> >> >> >> > which<br>
>> >> >> >> >> > is<br>
>> >> >> >> >> > different from what I intuitively imagine about it.<br>
>> >> >> >> >> ><br>
>> >> >> >> >> > Regards,<br>
>> >> >> >> >> > Barco<br>
>> >> >> >> >> > On Fri, Nov 18, 2011 at 1:49 PM, Dmitry Demeshchuk<br>
>> >> >> >> >> > <<a href="mailto:demeshchuk@gmail.com">demeshchuk@gmail.com</a>><br>
>> >> >> >> >> > wrote:<br>
>> >> >> >> >> >><br>
>> >> >> >> >> >> My guess is you have to zip them together, or just write a<br>
>> >> >> >> >> >> tail-recursed function:<br>
>> >> >> >> >> >><br>
>> >> >> >> >> >> map2(Fun, [H1 | T1], [H2 | T2]) -><br>
>> >> >> >> >> >>    [Fun(H1, H2) | map2(Fun, T1, T2)];<br>
>> >> >> >> >> >> map2(Fun, [], []) -><br>
>> >> >> >> >> >>    [].<br>
>> >> >> >> >> >><br>
>> >> >> >> >> >> The second option definitely isn't a list comprehension,<br>
>> >> >> >> >> >> but<br>
>> >> >> >> >> >> it<br>
>> >> >> >> >> >> requires less memory and has lesser complexity.<br>
>> >> >> >> >> >><br>
>> >> >> >> >> >> On Fri, Nov 18, 2011 at 9:45 AM, Barco You<br>
>> >> >> >> >> >> <<a href="mailto:barcojie@gmail.com">barcojie@gmail.com</a>><br>
>> >> >> >> >> >> wrote:<br>
>> >> >> >> >> >> > Dear Erlangers,<br>
>> >> >> >> >> >> ><br>
>> >> >> >> >> >> > I hope to get a list from two lists like this:<br>
>> >> >> >> >> >> > [{a1,b1}, {a2,b2}, {a3,b3}]      <-     [a1, a2 a3],<br>
>> >> >> >> >> >> >  [b1,<br>
>> >> >> >> >> >> > b2,<br>
>> >> >> >> >> >> > b3].<br>
>> >> >> >> >> >> > But if I use list comprehension, I got:<br>
>> >> >> >> >> >> > 10>  [{D1,D2} ||  D1 <- [a1,a2,a3], D2 <- [b1,b2,b3]].<br>
>> >> >> >> >> >> > [{a1,b1},<br>
>> >> >> >> >> >> >  {a1,b2},<br>
>> >> >> >> >> >> >  {a1,b3},<br>
>> >> >> >> >> >> >  {a2,b1},<br>
>> >> >> >> >> >> >  {a2,b2},<br>
>> >> >> >> >> >> >  {a2,b3},<br>
>> >> >> >> >> >> >  {a3,b1},<br>
>> >> >> >> >> >> >  {a3,b2},<br>
>> >> >> >> >> >> >  {a3,b3}]<br>
>> >> >> >> >> >> ><br>
>> >> >> >> >> >> > So, my questions is how to comprehend list in synchronous<br>
>> >> >> >> >> >> > way<br>
>> >> >> >> >> >> > in<br>
>> >> >> >> >> >> > order<br>
>> >> >> >> >> >> > to<br>
>> >> >> >> >> >> > get what I want, rather than to compose the elements from<br>
>> >> >> >> >> >> > two<br>
>> >> >> >> >> >> > lists<br>
>> >> >> >> >> >> > in<br>
>> >> >> >> >> >> > all<br>
>> >> >> >> >> >> > possible situations.<br>
>> >> >> >> >> >> ><br>
>> >> >> >> >> >> > Thank you,<br>
>> >> >> >> >> >> > Barco<br>
>> >> >> >> >> >> > _______________________________________________<br>
>> >> >> >> >> >> > erlang-questions mailing list<br>
>> >> >> >> >> >> > <a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
>> >> >> >> >> >> > <a href="http://erlang.org/mailman/listinfo/erlang-questions" target="_blank">http://erlang.org/mailman/listinfo/erlang-questions</a><br>
>> >> >> >> >> >> ><br>
>> >> >> >> >> >> ><br>
>> >> >> >> >> >><br>
>> >> >> >> >> >><br>
>> >> >> >> >> >><br>
>> >> >> >> >> >> --<br>
>> >> >> >> >> >> Best regards,<br>
>> >> >> >> >> >> Dmitry Demeshchuk<br>
>> >> >> >> >> ><br>
>> >> >> >> >> ><br>
>> >> >> >> >><br>
>> >> >> >> >><br>
>> >> >> >> >><br>
>> >> >> >> >> --<br>
>> >> >> >> >> Best regards,<br>
>> >> >> >> >> Dmitry Demeshchuk<br>
>> >> >> >> ><br>
>> >> >> >> ><br>
>> >> >> >><br>
>> >> >> >><br>
>> >> >> >><br>
>> >> >> >> --<br>
>> >> >> >> Best regards,<br>
>> >> >> >> Dmitry Demeshchuk<br>
>> >> >> ><br>
>> >> >> ><br>
>> >> >><br>
>> >> >><br>
>> >> >><br>
>> >> >> --<br>
>> >> >> Best regards,<br>
>> >> >> Dmitry Demeshchuk<br>
>> >> ><br>
>> >> ><br>
>> >><br>
>> >><br>
>> >><br>
>> >> --<br>
>> >> Best regards,<br>
>> >> Dmitry Demeshchuk<br>
>> ><br>
>> ><br>
>><br>
>><br>
>><br>
>> --<br>
>> Best regards,<br>
>> Dmitry Demeshchuk<br>
><br>
<br>
<br>
<br>
--<br>
Best regards,<br>
Dmitry Demeshchuk<br>
</blockquote></div>