3.常见警告 | 3. Common Caveats
3.共同注意事项
本节列出了需要注意的几个模块和BIF,不仅仅是从性能的角度来看。
3.1 定时器模块
使用erlang:send_after/3
和erlang:start_timer/3
,比使用在STDLIB中的timer
模块提供的计时器高效得多。计时器模块使用一个单独的进程来管理计时器。如果许多进程频繁地创建和取消计时器(特别是在使用SMP模拟器时),那么该进程很容易重载。
中相应的功能timer
模块不管理定时器(如timer:tc/3
或timer:sleep/1
),不叫定时器服务器进程,因此是无害的。
3.2 list_to_atom/1(字符串转原子)
原子不是垃圾收集的。一旦原子被创建,它就永远不会被移除。如果在默认情况下达到原子数的限制(默认为1,048,576),则模拟器终止。
因此,在一个连续运行的系统中,将任意输入字符串转换为原子可能是危险的。如果只允许某些定义明确的原子作为输入,list_to_existing_atom/1
可用于防范拒绝服务攻击。%28所有允许的原子必须是在前面创建的,例如,简单地使用模块中的所有原子并加载该模块。
使用list_to_atom/1
构造传递给apply/3
如下所示,非常昂贵,在时间关键的代码中不推荐:
apply(list_to_atom("some_prefix"++Var), foo, Args)
3.3 length/1(获取列表长度)
用于计算表的长度的时间正比于该列表的长度,而不是tuple_size/1
,byte_size/1
和bit_size/1
,其中所有在恒定的时间执行。
一般情况下,没有必要担心length/1
,因为它是在C中高效地实现的,在时间关键的代码中,如果输入列表可能很长,您可能希望避免它。
...的某些用途length/1
可以用匹配代替。例如,以下代码:
foo(L) when length(L) >= 3 ->
...
可重写为:
foo([_,_,_|_]=L) ->
...
一个细微的区别是length(L)
如果L
是一个不正确的列表,而第二个代码片段中的模式接受一个不正确的列表。
3.4 setelement/3(设置元素)
setelement/3
复制它修改的元组。因此,在循环中使用setelement/3
每次创建元组的新副本。
复制元组的规则有一个例外。如果编译器清楚地看到,破坏性地更新元组会产生与复制元组相同的结果,则调用setelement/3
被一种特殊的破坏性setelement
指令。在下面的代码序列中,第一个setelement/3
调用复制元组并修改第九个元素:
multiple_setelement(T0) ->
T1 = setelement(9, T0, bar),
T2 = setelement(7, T1, foobar),
setelement(5, T2, new_value).
以下两个setelement/3
调用修改元组的位置。
对于要应用的优化,全
下列条件必须是正确的:
- 索引必须是整数字面值,而不是变量或表达式。
- 指数必须按降序排列。
- 调用之间不得调用另一个函数。
setelement/3
...
- 从一次
setelement/3
呼叫返回的元组只能在随后的呼叫中使用setelement/3
。
如果代码不能按照multiple_setelement/1
例如,修改大型元组中的多个元素的最佳方法是将元组转换为列表,修改列表,并将其转换回元组。
3.5 size/1(获取元组/二进制)的大小
size/1
返回元组和二进制文件的大小。
使用BIFtuple_size/1
和byte_size/1
给编译器和运行时系统更多的优化机会。另一个优势是BIF为透析器提供了更多的类型信息。
3.6 split_binary/2
使用匹配而不是调用split_binary/2
功能。此外,混合位语法匹配和split_binary/2
可以防止某些位语法匹配的优化。
做
<<Bin1:Num/binary,Bin2/binary>> = Bin,
不要
{Bin1,Bin2} = split_binary(Bin, Num)
3.7 Operator "--"(运算符 --)
“--
“运算符的复杂度与其操作数长度的乘积成正比。这意味着如果运算符的两个操作数都是长列表,则运算符非常慢:
不要
HugeList1 -- HugeList2
相反,请使用ordsets
STDLIB中的模块:
做
HugeSet1 = ordsets:from_list(HugeList1),
HugeSet2 = ordsets:from_list(HugeList2),
ordsets:subtract(HugeSet1, HugeSet2)
显然,如果列表的原始顺序很重要,那么该代码就不能工作。如果必须保持列表的顺序,请按以下方式执行:
做
Set = gb_sets:from_list(HugeList2),
[E || E <- HugeList1, not gb_sets:is_element(E, Set)]
注
此代码的行为与“--
“如果列表中包含重复元素(在HugeList2中出现一个元素就会删除HugeList1中出现的所有元素)
此外,此代码将比较使用==
操作符,同时--
使用=:=
操作符。如果这种差异很重要,可以用gb_sets
来代替sets
,但是sets:from_list/1
比gb_sets:from_list/1
慢得多,gb_sets:from_list/1
用于长列表。
使用“--
“从列表中删除元素的运算符不存在性能问题:
好的
HugeList1 -- [Element]