erlang的坑

来源:互联网 发布:c语言 面向对象 编辑:程序博客网 时间:2024/05/06 05:55

    持续记录erlang中的各种坑以及各种解决方案。入门级别的同学都能说出erlang的优点,但是我们不能忽略erlang的各种缺点,一不小心可能造成大麻烦。

1、计算密集型代码执行瓶颈

    Erlang的虚拟机是register based的,性能上和python类似,和c语言大概有7倍的差距。这几乎是公认的事实,虽然大部分的集群和网络服务器,性能瓶颈在IO上面,而且这块erts(erlang运行期系统)做的非常的强大,但是一旦涉及到大量的计算,就有点麻烦了。纠结其根本原因,是从字节码到机器码这一部分。和java一样,一段erlang代码,先编译成字节码.beam文件(java里是.class)文件,在运行代码的时候,再根据不同的操作系统由虚拟机编译成相应的机器码(这一点貌似JVM比EVM强大)。java中有jit这样强大的支持,可以把转化机器码这部分的时间缩小,erlang呢?

在遇到计算密集型的需求时,通常的做法是nif,port。这两种机制原理差不多,用之前需要注意一下几点:
(1)确保C代码准确无误并且没有任何异常,否则后果就是整个节点挂掉。
(2)确保C代码没有耗时的操作,否则整个调度器将不能执行其他代码。(即执行nif时,其reduction不会改变,抢占不会发生,尽管我们可以使用一些方法bif中的trap机制,nif中增加reduction的方法,还可以用erlang:system_monitor监控这些方法来控制让出调度器,但是这些的开发成本很大)

如果对自己C代码功底怀疑,nif慎用。其他稳定高效的方法?hipe以及其加强版llvm(有些CPU不支持)。编译时加上native参数即可,例如c(test, [native, {hipe, [to_llvm]}]).

2、list的效率

    Erlang的list是最经常使用的一种数据结构,很多操作是基于遍历list来实现的,但是效率呢?10W数量的一个List遍历一次耗时30ms左右,难以接受。其他替代数据结构?
array:可以认为是一个能动态扩展的数组,取值——O(1),插入——O(1),遍历——比list好(越是稀疏的array越好)。
dict:dict是这样一个东西,他有一个segments,包含了所有dict数据(数据就是Key, Value}键值对),segments实际上就是一个tuple,这个segments的每一项是一个segment,segment就是长度为16的tuple(为什么是16,因为源码宏定义死的。。),segment的每一项是一个长度为3的list,list中的每个元素就是一个键值对了。如何决定一个{key, value}在segment的位置?根据erlang:phash(key, 16)的返回结果确定,如果某个位置的key超过3个怎么办?放到下一个segment。由此可以判断其效率,取值——O(1),插入——O(1),遍历——和list没区别(甚至比list要差)。
orddict:和dict完全是两个东西,其实就是一个排好序的tuplelist。所以取值——O(n),插入——O(n),遍历——和list一样。
gb_sets && gb_trees:这两种其实是一个东西,平衡二叉树实现,不同的是gb_sets可以存储任意数据,gb_trees只适合{key, value}结构数据。所以取值——O(logN),插入——O(logN),遍历——和list一致。
sets && ordsets:分别对应dict和orddict,不同的是,他们可以存储任意数据。
    从erlang的各种数据类型可以看出,无论何种数据结构,遍历都是一种耗时的操作,所以尽量避免遍历操作可以提升代码效率,另外无论何种数据结构都没有提供复杂的排序方案,没有复杂的取值和插入方法,所以在现实应用中,基本上还是根据当时的具体应用环境用list和tuple两种基础结构来自定义具体的数据结构。
http://blog.yufeng.info/archives/2688
0 0
原创粉丝点击