赞
踩
-module(test).
-compile(export_all).
non_opt_eq([H|T1], <<H,T2/binary>>) ->
non_opt_eq(T1, T2);
non_opt_eq([_|_], <<_,_/binary>>) ->
false;
non_opt_eq([], <<>>) ->
true.
opt_eq(<<H,T1/binary>>, [H|T2]) -> %% 分支1
opt_eq(T1, T2);
opt_eq(<<_,_/binary>>, [_|_]) -> %% 分支2
false;
opt_eq(<<>>, []) -> %% 分支3
true.
对比上面两个函数,依我个人习惯,在Erlang编程时会选择第一种写法。
test.erl:4: Warning: INFO: matching anything else but a plain variable to the left of binary pattern will prevent delayed sub binary optimization; SUGGEST changing argument order
test.erl:4: Warning: NOT OPTIMIZED: called function non_opt_eq/2 does not begin with a suitable binary matching instruction
test.erl:11: Warning: OPTIMIZED: creation of sub binary delayed
初看这些信息,还不太明白它说的是什么。为了弄明白,我们来分析一下它的汇编指令。
(关于汇编指令的介绍可参见《实例分析Erlang的汇编指令》一文)
%% test.S
{function, non_opt_eq, 2, 2}.
{label,1}.
{line,[{location,"test.erl",4}]}.
{func_info,{atom,test},{atom,non_opt_eq},2}.
{label,2}.
%% 如果R0为空列表,则返回true
{test,is_nonempty_list,{f,4},[{x,0}]}.
%% 从list中取头元素送R2
{get_list,{x,0},{x,2},{x,3}}.
%% 初始化match context二进制
{test,bs_start_match2,{f,1},4,[{x,1},0],{x,4}}.
%% 从二进制流中取出一个整型(1字节)
{test,bs_get_integer2,
{f,1},
5,
[{x,4},
{integer,8},
1,
{field_flags,[{anno,[4,{file,"test.erl"}]},unsigned,big]}],
{x,5}}.
%% 取出剩余的二进制,并创建新的sub binary,送R6
{test,bs_get_binary2,
{f,1},
6,
[{x,4},
{atom,all},
8,
{field_flags,[{anno,[4,{file,"test.erl"}]},unsigned,big]}],
{x,6}}.
%% '%'是注释,打印优化信息
{'%',{no_bin_opt,{<!-- -->{label,2},no_suitable_bs_start_match},
[4,{file,"test.erl"}]}}.
{test,is_eq_exact,{f,3},[{x,5},{x,2}]}.
%% R6(新创建的sub binary)送R1,为下一次迭代准备参数
{move,{x,6},{x,1}}.
{move,{x,3},{x,0}}.
{call_only,2,{f,2}}.
{label,3}.
{move,{atom,false},{x,0}}.
return.
{label,4}.
{test,is_nil,{f,1},[{x,0}]}.
{test,is_eq_exact,{f,1},[{x,1},{literal,<<>>}]}.
{move,{atom,true},{x,0}}.
return.
{function, opt_eq, 2, 6}.
{label,5}.
{line,[{location,"test.erl",11}]}.
{func_info,{atom,test},{atom,opt_eq},2}.
{label,6}.
%% 初始化match context二进制,结果送R0
{test,bs_start_match2,{f,5},2,[{x,0},0],{x,0}}.
%% 从二进制流中取出一个整型(1字节)
{test,bs_get_integer2,
{f,8},
2,
[{x,0},
{integer,8},
1,
{field_flags,[{anno,[11,{file,"test.erl"}]},unsigned,big]}],
{x,2}}.
{'%',{bin_opt,[11,{file,"test.erl"}]}}.
{test,bs_test_unit,{f,9},[{x,0},8]}.
%% R0送R3
{move,{x,0},{x,3}}.
{test,is_nonempty_list,{f,9},[{x,1}]}.
{get_list,{x,1},{x,4},{x,5}}.
{test,is_eq_exact,{f,7},[{x,4},{x,2}]}.
{move,{x,5},{x,1}}.
%% R3送回R0
{move,{x,3},{x,0}}.
{call_only,2,{f,6}}.
{label,7}.
{move,{atom,false},{x,0}}.
return.
{label,8}.
{test,bs_test_tail2,{f,9},[{x,0},0]}.
{test,is_nil,{f,9},[{x,1}]}.
{move,{atom,true},{x,0}}.
return.
{label,9}.
{bs_context_to_binary,{x,0}}.
{jump,{f,5}}.
上面的指令中可以明显看出 opt_eq函数的分支2没有创建sub binary,而是直接把match context类型的二进制作为下一次迭代的参数。
non_opt_eq([H|T1], <<H,T2/binary>>) ->
non_opt_eq(T1, T2);
non_opt_eq([_|_], T) ->
mod:fun(T).
match_body([0|_], <<H,_/binary>>) ->
done;
如果编译时加了优化选项,会提示你调整参数的顺序(SUGGEST changing argument order)。
小结
如果函数参数中有匹配操作的参数,根据实际的情况尽量将它放在首位。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。