首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

3.常见警告 | 3. Common Caveats

本节列出了需要注意的几个模块和BIF,不仅仅是从性能的角度来看。

3.1 定时器模块

使用erlang:send_after/3erlang:start_timer/3,比使用在STDLIB中的timer模块提供的计时器高效得多。计时器模块使用一个单独的进程来管理计时器。如果许多进程频繁地创建和取消计时器(特别是在使用SMP模拟器时),那么该进程很容易重载。

中相应的功能timer模块不管理定时器(如timer:tc/3timer:sleep/1),不叫定时器服务器进程,因此是无害的。

3.2 list_to_atom/1(字符串转原子)

原子不是垃圾收集的。一旦原子被创建,它就永远不会被移除。如果在默认情况下达到原子数的限制(默认为1,048,576),则模拟器终止。

因此,在一个连续运行的系统中,将任意输入字符串转换为原子可能是危险的。如果只允许某些定义明确的原子作为输入,list_to_existing_atom/1可用于防范拒绝服务攻击。%28所有允许的原子必须是在前面创建的,例如,简单地使用模块中的所有原子并加载该模块。

使用list_to_atom/1构造传递给apply/3如下所示,非常昂贵,在时间关键的代码中不推荐:

代码语言:javascript
复制
apply(list_to_atom("some_prefix"++Var), foo, Args)

3.3 length/1(获取列表长度)

用于计算表的长度的时间正比于该列表的长度,而不是tuple_size/1byte_size/1bit_size/1,其中所有在恒定的时间执行。

一般情况下,没有必要担心length/1,因为它是在C中高效地实现的,在时间关键的代码中,如果输入列表可能很长,您可能希望避免它。

...的某些用途length/1可以用匹配代替。例如,以下代码:

代码语言:javascript
复制
foo(L) when length(L) >= 3 ->
    ...

可重写为:

代码语言:javascript
复制
foo([_,_,_|_]=L) ->
   ...

一个细微的区别是length(L)如果L是一个不正确的列表,而第二个代码片段中的模式接受一个不正确的列表。

3.4 setelement/3(设置元素)

setelement/3复制它修改的元组。因此,在循环中使用setelement/3每次创建元组的新副本。

复制元组的规则有一个例外。如果编译器清楚地看到,破坏性地更新元组会产生与复制元组相同的结果,则调用setelement/3被一种特殊的破坏性setelement指令。在下面的代码序列中,第一个setelement/3调用复制元组并修改第九个元素:

代码语言:javascript
复制
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/1byte_size/1给编译器和运行时系统更多的优化机会。另一个优势是BIF为透析器提供了更多的类型信息。

3.6 split_binary/2

分割二进制/2

使用匹配而不是调用split_binary/2功能。此外,混合位语法匹配和split_binary/2可以防止某些位语法匹配的优化。

代码语言:javascript
复制
<<Bin1:Num/binary,Bin2/binary>> = Bin,

不要

代码语言:javascript
复制
{Bin1,Bin2} = split_binary(Bin, Num)

3.7 Operator "--"(运算符 --)

--“运算符的复杂度与其操作数长度的乘积成正比。这意味着如果运算符的两个操作数都是长列表,则运算符非常慢:

不要

代码语言:javascript
复制
HugeList1 -- HugeList2

相反,请使用ordsetsSTDLIB中的模块:

代码语言:javascript
复制
HugeSet1 = ordsets:from_list(HugeList1),
HugeSet2 = ordsets:from_list(HugeList2),
ordsets:subtract(HugeSet1, HugeSet2)

显然,如果列表的原始顺序很重要,那么该代码就不能工作。如果必须保持列表的顺序,请按以下方式执行:

代码语言:javascript
复制
Set = gb_sets:from_list(HugeList2),
[E || E <- HugeList1, not gb_sets:is_element(E, Set)]

此代码的行为与“--“如果列表中包含重复元素(在HugeList2中出现一个元素就会删除HugeList1中出现的所有元素)

此外,此代码将比较使用==操作符,同时--使用=:=操作符。如果这种差异很重要,可以用gb_sets来代替sets,但是sets:from_list/1gb_sets:from_list/1慢得多,gb_sets:from_list/1用于长列表。

使用“--“从列表中删除元素的运算符不存在性能问题:

好的

代码语言:javascript
复制
HugeList1 -- [Element]

扫码关注腾讯云开发者

领取腾讯云代金券