簡體   English   中英

Erlang,Last Call Optimization,lambda函數以及如何防止堆棧增加

[英]Erlang, Last Call Optimization, lambda functions, and how to prevent growing a stack

我正在編寫一些Erlang代碼,但遇到一種我不理解的怪異情況。

編碼:

-module(recursive_test).
-export([a/2]).

a(_, []) -> ok;
a(Args, [H|T]) ->
    F = fun() -> a(Args, T) end,
    io:fwrite(
        "~nH: ~p~nStack Layers: ~p",
        [H, process_info(self(), stack_size)]
    ),
    b(Args, F).

b(Args, F) ->
    case Args of
        true -> ok;
        false -> F()
    end.

輸出:

(Erlang/OTP 20)
12> c(recursive_test).
{ok,recursive_test}
13> recursive_test:a(false, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).
H: 1
Stack Layers: 28
H: 2
Stack Layers: 28
H: 3
Stack Layers: 28
H: 4
Stack Layers: 28
H: 5
Stack Layers: 28
H: 6
Stack Layers: 28
H: 7
Stack Layers: 28
H: 8
Stack Layers: 28
H: 9
Stack Layers: 28
H: 10
Stack Layers: 28
ok
14> recursive_test:a(false, [1, 2, 3, 4, 5, 6]).
H: 1
Stack Layers: 28
H: 2
Stack Layers: 28
H: 3
Stack Layers: 28
H: 4
Stack Layers: 28
H: 5
Stack Layers: 28
H: 6
Stack Layers: 28
ok

從我的理解這篇文章 ,二郎使用最后調用優化的地方,如果過去的事情的功能確實是調用另一個函數,該BeamVM反而會跳程序計數器的新功能的開始,而不是推一個新堆的幀。 這是否意味着在上述模式中,我們踩着堆而不是踩着堆? 這是否釋放了以前保存在這些函數中的內存(對於上述代碼,是一次在內存中分配一個函數F的副本,還是一次在內存中分配多個函數F的副本?一次)? 使用此模式是否有任何負面影響(除了明顯的調試困難之外)?

首先,由於Erlang的不可變性質, fun (lambda,closure或任何您想稱呼的東西)可以並且可以像元組一樣想象實現

{fun, {Module, FuncRef, Arity, CodeVersion}, CapturedValues}

因此,在您的情況下,它將類似於

{fun, {recursive_test, '-a/2-fun-0-', 2, 2248400...}, [false, [2,3,4|...]]}

注意,因為您有0的fun加上2個捕獲的值,所以arity為2。

然后,應該更容易理解代碼中正在發生的事情。 請記住,不是真正的元組,而是一些在數據消耗,堆術語引用,GC,圍繞Erlang分發協議進行傳輸等方面表現非常相似的結構。

讓您了解第二件事,即在b/2中調用F()類似於

recursive_test:'-a/2-fun-0-'(false, [2,3,4|...])

這可以是很好的尾叫又名跳轉。 因此,您的代碼使“元組”變得fun ,然后在代碼中跳來跳去。 然后不再引用每個fun “元組”,因此可以隨時將其刪除。 我建議您嘗試使用數字而不是列表進行嘗試,並嘗試使用越來越大的數字,並使用process_info或觀察器觀察內存消耗。 這將是一個很好的練習。

可以使用的順便說一句

process_info(self(), stack_size)

而不是緩慢而丑陋

roplists:get_value(stack_size, process_info(self()))

這不是答案,但是您應該找到想要的東西。 我已經編譯了您的代碼的一個版本(沒有io:format調用第7行。然后,您可以反編譯Beam文件以查看代碼的解釋方式:

-module(recursive_test).
-export([a/2]).

a(_, []) -> ok;
a(Args, [H|T]) ->
    F = fun() ->
            a(Args, T)
        end,
    b(Args, F).

b(Args, F) ->
    case Args of
        true -> ok;
        false -> F()
    end.

在外殼中:

15> c(recursive_test).                      
recursive_test.erl:5: Warning: variable 'H' is unused
{ok,recursive_test}
16> rp(beam_disasm:file(recursive_test)).
{beam_file,recursive_test,
           [{a,2,2},{module_info,0,10},{module_info,1,12}],
           [{vsn,[224840029366305056373101858936888814401]}],
           [{version,"7.2.1"},
            {options,[]},
            {source,"c:/git/fourretout/src/recursive_test.erl"}],
           [{function,a,2,2,
                      [{label,1},
                       {line,1},
                       {func_info,{atom,recursive_test},{atom,a},2},
                       {label,2},
                       {test,is_nonempty_list,{f,3},[{x,1}]},
                       {allocate,1,2},
                       {get_tl,{x,1},{x,1}},
                       {move,{x,0},{y,0}},
                       {make_fun2,{recursive_test,'-a/2-fun-0-',2},0,88683754,2},
                       {move,{x,0},{x,1}},
                       {move,{y,0},{x,0}},
                       {call_last,2,{recursive_test,b,2},1},
                       {label,3},
                       {test,is_nil,{f,1},[{x,1}]},
                       {move,{atom,ok},{x,0}},
                       return]},
            {function,b,2,5,
                      [{line,2},
                       {label,4},
                       {func_info,{atom,recursive_test},{atom,b},2},
                       {label,5},
                       {test,is_atom,{f,8},[{x,0}]},
                       {select_val,{x,0},
                                   {f,8},
                                   {list,[{atom,true},{f,6},{atom,false},{f,7}]}},
                       {label,6},
                       {move,{atom,ok},{x,0}},
                       return,
                       {label,7},
                       {allocate,0,2},
                       {move,{x,1},{x,0}},
                       {line,3},
                       {call_fun,0},
                       {deallocate,0},
                       return,
                       {label,8},
                       {line,4},
                       {case_end,{x,0}}]},
            {function,module_info,0,10,
                      [{line,0},
                       {label,9},
                       {func_info,{atom,recursive_test},{atom,module_info},0},
                       {label,10},
                       {move,{atom,recursive_test},{x,0}},
                       {line,0},
                       {call_ext_only,1,{extfunc,erlang,get_module_info,1}}]},
            {function,module_info,1,12,
                      [{line,0},
                       {label,11},
                       {func_info,{atom,recursive_test},{atom,module_info},1},
                       {label,12},
                       {move,{x,0},{x,1}},
                       {move,{atom,recursive_test},{x,0}},
                       {line,0},
                       {call_ext_only,2,{extfunc,erlang,get_module_info,2}}]},
            {function,'-a/2-fun-0-',2,14,
                      [{line,5},
                       {label,13},
                       {func_info,{atom,recursive_test},{atom,'-a/2-fun-0-'},2},
                       {label,14},
                       {call_only,2,{recursive_test,a,2}}]}]}
ok
17>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM