0%

Erlang中的一些测试结果

概述

Erlang中的一些测试结果

1.匿名函数重新生成

测试环境
Erlang/OTP 17 [erts-6.0] [64-bit] [smp:6:6] [async-threads:10]

1
2
3
4
5
[lists:foldl(fun(X, Sum) -> X + Sum end, 0, [1,2,3,4,5]) || X <- lists:seq(1,5)].
和下面
Fun = fun(X, Sum) -> X + Sum end.
#Fun<erl_eval.12.82930912>
[lists:foldl(Fun, 0, [1,2,3,4,5]) || X <- lists:seq(1,5)].

看到大佬们17年的博客说到用方式1每次循环匿名函数都会重新生成

测试下看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
-module(test_anonymous_func).
-compile(export_all).
-define(DEBUG(F), io:format("##[~w~w:~w] " ++ F ++ "~n", [self(), ?MODULE, ?LINE])).
-define(DEBUG(F, A), io:format("##[~w~w:~w] " ++ F ++ "~n", [self(), ?MODULE, ?LINE | A])).
%%======================测试匿名函数是否会重新生成=======================================
test_func(Type, Num) ->
case Type of
1 -> %% 这里的F每次都会重新生成?
myfoldl(fun(X, Items) ->
[X | Items]
end, [], lists:seq(1, Num));
2 ->
F = fun(X, Items) ->
[X | Items]
end,
myfoldl(F, [], lists:seq(1, Num))
end.

%% 从lists:foldl复制出来的
myfoldl(F, Accu, [Hd|Tail]) ->
?DEBUG("make_ref:~p end",[F]),
myfoldl(F, F(Hd, Accu), Tail);
myfoldl(F, Accu, []) when is_function(F, 2) -> Accu.

%% 从源码可以看出 A ++ B的时候会遍历A 那么如果先对AB length一下再传参会怎么样?
%% timer:tc(lists,append,[lists:seq(1,100000000),[1]]).
%% timer:tc(lists,append,[[1],lists:seq(1,100000000)]).
%% timer:tc(erlang,length,[lists:seq(1,1000000000)]).
%% timer:tc(test_anonymous_func,test_append,[lists:seq(1,100000000),[1]]).
%% timer:tc(test_anonymous_func,test_append,[[1],lists:seq(1,100000000)]).
test_append(A, B) ->
case length(A) > length(B) of
true ->
lists:append(B, A);
false ->
lists:append(A, B)
end.

图示

img_4.png

分析

从打印的make_ref看匿名函数没有重新生成,官网已经下载不到更低的版本了,遂放弃,不知道是在OTP 17以后编译器做了优化还是要到底层的汇编码去找区别,因个人能力有限,只能测到这步,如有了解汇编的大佬还请指点一二

img_3.png

2.lists:append执行时间分析

图示

img_5.png

分析

从图可以看出来速度差了有10倍,length这个nif函数Erlang其实已经优化得很好了,为什么lists:append不先通过成length排序再把长度短的放前面查入呢?

3.ets取数据

1)测试下match

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ets:new(table, [named_table, bag]).

ets:tab2list(table).


ets:insert(table,[
{items,a,b,c,d},
{items,a,b,c,a},
{items,a,a,c,a},
{items,a,b,c,a,p},
{cat,brown,soft,loveable,selfish},
{friends,[jenn,jeff,etc]},
{items,1,2,3,1}
]).

ets:insert(table,{items,q,q,c,q}).
ets:lookup(table, items).

ets:match(table,{items,'$1','$2','_','$1'}).
ets:match(table,{items,'$114','$212','_','$6'}).
ets:match_object(table,{items,'$1','$2','_','$1'}).
可以看到{items,a,b,c,a,p}没有被取出来 说明 {items,'$1','$2','_','$1'} 这里还限制了数量
[{items,a,b,c,a},
{items,a,a,c,a},
{items,1,2,3,1},
{items,q,q,c,q}]

ets:insert_new(table, {items,1,3,2,9}).

2)测试下 insert_new 在 set 下的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ets:new(ingredients,[set, named_table]).

ets:insert(ingredients,{abacon,cgreat,tsfw}).

ets:insert(ingredients,{mbacon,ygreat,kasfw}).

ets:insert_new(ingredients,{abacon,aaa,qqq}).
false

ets:insert_new(ingredients,[{bacon,good},{cabbage,alright},{cawf,good}]).
true

ets:insert_new(ingredients,[{bacon,good},{abacon,aaa,qqq},{cabbage,alright},{cawf,good}]).
false
可以看到只要里面有一样的主键就会insert失败

3)测试下select的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ets:fun2ms(fun(X) -> X end).
ets:fun2ms(fun({X,Y}) -> X + Y end).
ets:fun2ms(fun({X,Y}) when X < Y -> X + Y end).
ets:fun2ms(fun({X,Y}) when X < Y , X rem 2 == 0 -> X + Y end).
ets:fun2ms(fun({X,Y}) when X < Y , X rem 2 == 0; Y == 0 -> X + Y end).


rd(food,{name,calories,price,group}).

ets:fun2ms(fun(N = #food{calories = C}) when C < 600 -> N end).

ets:new(food,[ordered_set,{keypos,#food.name},named_table]).
ets:insert(food,[
#food{name=salmon,calories = 88, price = 4.00,group = meat},
#food{name=cereals,calories = 178, price = 2.79,group = bread},
#food{name=milk,calories = 150, price = 3.23,group = dairy},
#food{name=cake,calories = 650, price = 7.21,group = delicious},
#food{name=bacon,calories = 800, price = 6.32,group = meat},
#food{name=sandwich,calories = 550, price = 5.78,group = whatever}
]).


ets:select(food,ets:fun2ms(fun(N = #food{calories = C}) when C < 600 -> N end)).

%% 逆序输出
ets:select_reverse(food,ets:fun2ms(fun(N = #food{calories = C}) when C < 600 -> N end)).

ets:select(food,ets:fun2ms(fun(N = #food{group = delicious}) -> N end)).

ets:select_delete(food,ets:fun2ms(fun(N = #food{price = P}) when P > 5 -> true end)).



ets:select_reverse(food,ets:fun2ms(fun(N = #food{calories = C}) when C < 600 -> N end)).

4)测试下const的用法

const会在导入环境中的变量时生成
官网关于const的一些资料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Tab = ets:new(tab, []).

ets:insert(Tab,{3,{4, 5}}).


ets:fun2ms(fun({M,N}) when N == {4, 5} -> M end).
[{{'$1','$2'},[{'==','$2',{{4,5}}}],['$1']}]


ets:select(Tab,ets:fun2ms(fun({M,N}) when N == {4, 5} -> M end)).

ets:select(Tab,[{{'$1','$2'},[{'==','$2',{const, {4,5}}}],['$1']}]).


%% 可以看到这里直接报错了
catch ets:select(Tab,[{{'$1','$2'},[{'==','$2',{4,5}}],['$1']}]).